Package: autoconf
Version: 2.67-2
Severity: normal

Hi!

I get the very same problem here.

Here is the code produced by an older version of autoconf:

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking CFLAGS for gcc -std=gnu99" >&5
$as_echo_n "checking CFLAGS for gcc -std=gnu99... " >&6; }
if test "${ac_cv_cflags_gcc_option__std_gnu99+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_cv_cflags_gcc_option__std_gnu99="no, unknown"

With autoconf 2.67, we get:

as_VAR=`$as_echo "ac_cv_cflags_gcc_option_-std=gnu99" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking CFLAGS for gcc -std=gnu99" >&5
$as_echo_n "checking CFLAGS for gcc -std=gnu99... " >&6; }
if eval "test \"\${$as_VAR+set}\"" = set; then :
  $as_echo_n "(cached) " >&6
else
  $as_VAR="no, unknown"

I suppose that $as_VAR is an invalid assignment:

$ i=j
$ $i=4
sh: j=4: not found

I don't understand why autoconf is doing the substitution at runtime
instead of doing statically like it was doing before.
AX_CFLAGS_GCC_OPTION macro starts with:

AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)],
VAR,[VAR="no, unknown"
 AC_LANG_SAVE

Maybe the assignment to VAR should be replaed by AS_VAR_SET call? VAR
is also used with "$VAR". Nested substitution is not allowed. This
could be replaced with AS_VAR_COPY.

I attach a patch that I got by trial and error. Notably, I use ac_res
as a variable to store the content of VAR. I don't know if it is a
safe method since I am no autoconf guru.

-- System Information:
Debian Release: squeeze/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.32-5-amd64 (SMP w/1 CPU core)
Locale: LANG=fr_FR.utf8, LC_CTYPE=fr_FR.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages autoconf depends on:
ii  debianutils                   3.4        Miscellaneous utilities specific t
ii  m4                            1.4.14-3   a macro processing language
ii  perl                          5.10.1-14  Larry Wall's Practical Extraction 

Versions of packages autoconf recommends:
ii  automake [automaken]    1:1.11.1-1       A tool for generating GNU Standard
ii  automake1.9 [automaken] 1.9.6+nogfdl-3.1 A tool for generating GNU Standard

Versions of packages autoconf suggests:
ii  autoconf-archive            20090426-1   The Autoconf Macro Archive
ii  autoconf-doc                2.67-1       automatic configure script builder
pn  autoconf2.13                <none>       (no description available)
ii  gettext                     0.18.1.1-1   GNU Internationalization utilities
ii  gnu-standards               2010.03.11-1 GNU coding and package maintenance
ii  libtool                     2.2.6b-2     Generic library support script

-- no debconf information
diff --git a/CHANGELOG b/CHANGELOG
index c92a9dc..bc30ad8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+lldpd (0.5.2)
+  * Features:
+    + More flexible smart mode and new default. Manual page has been updated.
+
 lldpd (0.5.1)
   * Features:
     + Allow to force a protocol even when no peer for this protocol is
diff --git a/m4/ax_cflags_gcc_option.m4 b/m4/ax_cflags_gcc_option.m4
index d5e6cd1..c01daef 100644
--- a/m4/ax_cflags_gcc_option.m4
+++ b/m4/ax_cflags_gcc_option.m4
@@ -76,7 +76,7 @@ AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl
 AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
 AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl
 AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)],
-VAR,[VAR="no, unknown"
+VAR,[AS_VAR_SET([VAR], ["no, unknown"])
  AC_LANG_SAVE
  AC_LANG_C
  ac_save_[]FLAGS="$[]FLAGS"
@@ -86,19 +86,20 @@ in "-pedantic -Werror % m4_ifval($2,$2,-option)"  dnl   GCC
    #
 do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
    AC_TRY_COMPILE([],[return 0;],
-   [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+   [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]) ; break])
 done
  FLAGS="$ac_save_[]FLAGS"
  AC_LANG_RESTORE
 ])
-case ".$VAR" in
+AS_VAR_COPY([ac_res], [VAR])
+case ".${ac_res}" in
      .ok|.ok,*) m4_ifvaln($3,$3) ;;
    .|.no|.no,*) m4_ifvaln($4,$4) ;;
    *) m4_ifvaln($3,$3,[
-   if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
-   then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
-   else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
-                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
+   if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " ${ac_res} " 2>&1 >/dev/null
+   then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain ${ac_res}])
+   else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) ${ac_res}"])
+                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) ${ac_res}"
    fi ]) ;;
 esac
 AS_VAR_POPDEF([VAR])dnl
@@ -112,7 +113,7 @@ AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_OLD], [dnl
 AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
 AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$2])dnl
 AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)],
-VAR,[VAR="no, unknown"
+VAR,[AS_VAR_SET([VAR],["no, unknown"])
  AC_LANG_SAVE
  AC_LANG_CPLUSPLUS
  ac_save_[]FLAGS="$[]FLAGS"
@@ -122,19 +123,20 @@ in "-pedantic -Werror % m4_ifval($2,$2,-option)"  dnl   GCC
    #
 do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
    AC_TRY_COMPILE([],[return 0;],
-   [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+   [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]) ; break])
 done
  FLAGS="$ac_save_[]FLAGS"
  AC_LANG_RESTORE
 ])
-case ".$VAR" in
+AS_VAR_COPY([ac_res], [VAR])
+case ".${ac_res}" in
      .ok|.ok,*) m4_ifvaln($3,$3) ;;
    .|.no|.no,*) m4_ifvaln($4,$4) ;;
    *) m4_ifvaln($3,$3,[
-   if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
-   then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
-   else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
-                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
+   if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " ${VAR} " 2>&1 >/dev/null
+   then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain ${ac_res}])
+   else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) ${ac_res}"])
+                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) ${ac_res}"
    fi ]) ;;
 esac
 AS_VAR_POPDEF([VAR])dnl
@@ -147,7 +149,7 @@ AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl
 AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
 AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl
 AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
-VAR,[VAR="no, unknown"
+VAR,[AS_VAR_SET([VAR],["no, unknown"])
  AC_LANG_SAVE
  AC_LANG_C
  ac_save_[]FLAGS="$[]FLAGS"
@@ -157,19 +159,20 @@ in "-pedantic -Werror % m4_ifval($1,$1,-option)"  dnl   GCC
    #
 do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
    AC_TRY_COMPILE([],[return 0;],
-   [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+   [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]) ; break])
 done
  FLAGS="$ac_save_[]FLAGS"
  AC_LANG_RESTORE
 ])
-case ".$VAR" in
+AS_VAR_COPY([ac_res], [VAR])
+case ".${ac_res}" in
      .ok|.ok,*) m4_ifvaln($3,$3) ;;
    .|.no|.no,*) m4_ifvaln($4,$4) ;;
    *) m4_ifvaln($3,$3,[
-   if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
-   then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
-   else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
-                      m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
+   if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " ${ac_res} " 2>&1 >/dev/null
+   then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain ${ac_res}])
+   else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) ${ac_res}"])
+                      m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) ${ac_res}"
    fi ]) ;;
 esac
 AS_VAR_POPDEF([VAR])dnl
@@ -183,7 +186,7 @@ AC_DEFUN([AX_CXXFLAGS_GCC_OPTION_NEW], [dnl
 AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
 AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_gcc_option_$1])dnl
 AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
-VAR,[VAR="no, unknown"
+VAR,[AS_VAR_SET([VAR],["no, unknown"])
  AC_LANG_SAVE
  AC_LANG_CPLUSPLUS
  ac_save_[]FLAGS="$[]FLAGS"
@@ -193,19 +196,20 @@ in "-pedantic -Werror % m4_ifval($1,$1,-option)"  dnl   GCC
    #
 do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
    AC_TRY_COMPILE([],[return 0;],
-   [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+   [AS_VAR_SET([VAR],[`echo $ac_arg | sed -e 's,.*% *,,'`]) ; break])
 done
  FLAGS="$ac_save_[]FLAGS"
  AC_LANG_RESTORE
 ])
-case ".$VAR" in
+AS_VAR_COPY([ac_res], [VAR])
+case ".${ac_res}" in
      .ok|.ok,*) m4_ifvaln($3,$3) ;;
    .|.no|.no,*) m4_ifvaln($4,$4) ;;
    *) m4_ifvaln($3,$3,[
-   if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
-   then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
-   else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
-                      m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
+   if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " ${ac_res} " 2>&1 >/dev/null
+   then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain ${ac_res}])
+   else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) ${ac_res}"])
+                      m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) ${ac_res}"
    fi ]) ;;
 esac
 AS_VAR_POPDEF([VAR])dnl
diff --git a/man/lldpd.8 b/man/lldpd.8
index bb57473..ae3d8d2 100644
--- a/man/lldpd.8
+++ b/man/lldpd.8
@@ -132,6 +132,11 @@ will still receive (and publish using SNMP if enabled) those LLDP-MED
 TLV but will not send them. Use this option if you don't want to
 transmit sensible information like serial numbers.
 .It Fl H Ar hide
+Filter neighbors. See section
+.Sx FILTERING NEIGHBORS
+for details.
+.El
+.Sh FILTERING NEIGHBORS
 In a heterogeneous network, you may see several different hosts on the
 same port, even if there is only one physically plugged to this
 port. For example, if you have a Nortel switch running LLDP which is
@@ -143,40 +148,92 @@ want. The
 parameter will allow you to tell
 .Nm
 to discard some frames that it receives and to avoid to send some
-other frames. The rationale behind the possible modes is that we
-should guess which protocol the equipment we are linked with is
-speaking. Moreover, it can speak several protocols. The main idea used
-is that if we receive on one port one CDP frame and three LLDP frames,
-we assume that the equipment is speaking CDP and that LLDP frames are
-just flooded through this equipment. The possible values are:
-.Bl -tag -width "XX"
-.It Sy 0
-Do not be smart, do not filter any frame
-.It Sy 1
-For each port, get the protocol with less neighbors and use only
-this protocol for reception and sending; in case of a tie, LLDP
-protocol wins. This is the default mode.
-.It Sy 2
-For each port, get the protocol with less neighbors and use only
-this protocol for reception; in case of a tie, LLDP protocol wins. No
-frame is filtered when sending.
-.It Sy 3
-For each port, get the protocol with less neighbors and use only this
-protocol for sending; in case of a tie, LLDP protocol wins. No frame
-is filtered on reception.
-.It Sy 4
-Same as 1 but in case of a tie, both protocols win.
-.It Sy 5
-Same as 2 but in case of a tie, both protocols win.
-.It Sy 6
-Same as 3 but in case of a tie, both protocols win.
-.It Sy 7
-Same as 1 but only one neighbor is kept.
-.It Sy 8
-Same as 2 but only one neighbor is kept.
-.It Sy 9
-Same as 3 but only one neighbor is kept.
-.El
+other frames.
+.Pp
+Incoming filtering and outgoing filtering are
+unrelated. Incoming filtering will hide some remote ports to get you a
+chance to know exactly what equipment is on the other side of the
+network cable. Outgoing filtering will avoid to use some protocols to
+avoid flooding your network with a protocol that is not handled by the
+nearest equipment. Keep in mind that even without filtering,
+.Nm
+will speak protocols for which at least one frame has been received
+and LLDP otherwise (there are other options to change this behaviour,
+for example
+.Fl cc , ss , ee , ll
+and
+.Fl ff
+).
+.Pp
+When enabling incoming filtering,
+.Nm
+will try to select one protocol and filter out neighbors using other
+protocols. To select this protocol, the rule is to take the less used
+protocol. If on one port, you get 12 CDP neighbors and 1 LLDP
+neighbor, this mean that the remote switch speaks LLDP and does not
+filter CDP. Therefore, we select LLDP. When enabling outgoing
+filtering,
+.Nm
+will also try to select one protocol and only speaks this
+protocol. The filtering is done per port. Each port may select a
+different protocol.
+.Pp
+There are two additional criteria when enabling filtering: allowing
+one or several protocols to be selected (in case of a tie) and
+allowing one or several neighbors to be selected. Even when allowing
+several protocols, the rule of selecting the protocols with the less
+neighbors still apply. If
+.Nm
+selects LLDP and CDP, this means they have the same number of
+neighbors. The selection of the neighbor is random. Incoming filtering
+will select a set of neighbors to be displayed while outgoing
+filtering will use the selected set of neighbors to decide which
+protocols to use: if a selected neighbor speaks LLDP and another one
+CDP,
+.Nm
+will speak both CDP and LLDP on this port.
+.Pp
+There are some corner cases. A typical example is a switch speaking
+two protocols (CDP and LLDP for example). You want to get the
+information from the best protocol but you want to speak both
+protocols because some tools use the CDP table and some other the LLDP
+table.
+.Pp
+The table below summarize all accepted values for the
+.Fl H Ar hide
+parameter. The default value is
+.Em 15
+which corresponds to the corner case described above. The
+.Em filter
+column means that filtering is enabled. The
+.Em 1proto
+column tells that only one protocol will be kept. The
+.Em 1neigh
+column tells that only one neighbor will be kept.
+.Pp
+.Bl -column -compact -offset indent "HXXX" "filterX" "1protoX" "1neighX" "filterX" "1protoX" "1neighX"
+.It Ta Ta incoming Ta Ta outgoing Ta
+.It Ta Em filter Ta Em 1proto Ta Em 1neigh Ta Em filter Ta Em 1proto Ta Em 1neigh
+.It Em 0  Ta   Ta   Ta   Ta   Ta   Ta  
+.It Em 1  Ta x Ta x Ta   Ta x Ta x Ta  
+.It Em 2  Ta x Ta x Ta   Ta   Ta   Ta  
+.It Em 3  Ta   Ta   Ta   Ta x Ta x Ta  
+.It Em 4  Ta x Ta   Ta   Ta x Ta   Ta  
+.It Em 5  Ta x Ta   Ta   Ta   Ta   Ta  
+.It Em 6  Ta   Ta   Ta   Ta x Ta   Ta  
+.It Em 7  Ta x Ta x Ta x Ta x Ta x Ta  
+.It Em 8  Ta x Ta x Ta x Ta   Ta   Ta  
+.It Em 9  Ta x Ta   Ta x Ta x Ta x Ta  
+.It Em 10 Ta   Ta   Ta   Ta x Ta   Ta x
+.It Em 11 Ta x Ta   Ta x Ta   Ta   Ta  
+.It Em 12 Ta x Ta   Ta x Ta x Ta   Ta x
+.It Em 13 Ta x Ta   Ta x Ta x Ta   Ta  
+.It Em 14 Ta x Ta x Ta   Ta x Ta   Ta x
+.It Em 15 Ta x Ta x Ta   Ta x Ta   Ta  
+.It Em 16 Ta x Ta x Ta x Ta x Ta   Ta x
+.It Em 17 Ta x Ta x Ta x Ta x Ta   Ta  
+.It Em 18 Ta x Ta   Ta   Ta x Ta   Ta x
+.It Em 19 Ta x Ta   Ta   Ta x Ta x Ta  
 .El
 .Sh FILES
 .Bl -tag -width "/var/run/lldpd.socketXX" -compact
diff --git a/src/lldpd.c b/src/lldpd.c
index e11ea57..ed5b26c 100644
--- a/src/lldpd.c
+++ b/src/lldpd.c
@@ -82,6 +82,7 @@ static void		 lldpd_exit(void);
 static void		 lldpd_send_all(struct lldpd *);
 static void		 lldpd_recv_all(struct lldpd *);
 static void		 lldpd_hide_all(struct lldpd *);
+static void		 lldpd_hide_ports(struct lldpd *, struct lldpd_hardware *, int);
 static int		 lldpd_guess_type(struct lldpd *, char *, int);
 static void		 lldpd_decode(struct lldpd *, char *, int,
 			    struct lldpd_hardware *);
@@ -587,67 +588,87 @@ static void
 lldpd_hide_all(struct lldpd *cfg)
 {
 	struct lldpd_hardware *hardware;
+
+	TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+		lldpd_hide_ports(cfg, hardware, SMART_RECEPTION);
+		lldpd_hide_ports(cfg, hardware, SMART_EMISSION);
+	}
+}
+
+static void
+lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) {
 	struct lldpd_port *port;
 	int protocols[LLDPD_MODE_MAX+1];
 	int i, j, found;
 	unsigned int min;
 
-	TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
-		/* Compute the number of occurrences of each protocol */
-		for (i = 0; i <= LLDPD_MODE_MAX; i++)
-			protocols[i] = 0;
-		TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
-			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];
+	/* Compute the number of occurrences of each protocol */
+	for (i = 0; i <= LLDPD_MODE_MAX; i++)
+		protocols[i] = 0;
+	TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
+		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];
+	found = 0;
+	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_smart & mask &
+			    (SMART_EMISSION_ONE_PROTO | SMART_RECEPTION_ONE_PROTO))
+				found = 1;
+			protocols[i] = 1;
+		} else protocols[i] = 0;
+
+	/* We set the p_hidden flag to 1 if the protocol is disabled */
+	TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+		if (mask == SMART_EMISSION)
+			port->p_hidden_snd = protocols[port->p_protocol]?0:1;
+		else
+			port->p_hidden_rcv = protocols[port->p_protocol]?0:1;
+	}
+
+	/* If we want only one neighbor, we take the first one */
+	if (cfg->g_smart & mask &
+	    (SMART_EMISSION_ONE_NEIGH | SMART_RECEPTION_ONE_NEIGH)) {
 		found = 0;
-		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_smart & SMART_FILTER_NO_TIE)
+		TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+			if (((mask == SMART_EMISSION) && !port->p_hidden_snd) ||
+			    ((mask == SMART_RECEPTION) && !port->p_hidden_rcv)) {
+				if (found && mask == SMART_EMISSION)
+					port->p_hidden_snd = 1;
+				else if (found && mask == SMART_RECEPTION)
+					port->p_hidden_rcv = 1;
+				else
 					found = 1;
-				protocols[i] = 1;
-			} else protocols[i] = 0;
-
-		/* We set the p_hidden flag to 1 if the protocol is disabled */
-		TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
-			port->p_hidden = protocols[port->p_protocol]?0:1;
-
-		/* If we want only one neighbor, we take the first one */
-		if (cfg->g_smart & SMART_FILTER_ONE_NEIGH) {
-			found = 0;
-			TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
-				if (!port->p_hidden) {
-					if (found)
-						port->p_hidden = 1;
-					else
-						found = 1;
-				}
 			}
 		}
+	}
 
-		/* Print a debug message summarizing the operation */
-		i = j = 0;
-		TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
-		    if (port->p_hidden) i++;
-		    j++;
-		}
-		if (i) {
-			LLOG_DEBUG("On %s, out of %d neighbors, %d are hidden",
-			    hardware->h_ifname, j, i);
-			for (i=0; protos[i].mode != 0; i++) {
-				if (protos[i].enabled)
-					LLOG_DEBUG("On %s, %s is %s",
-					    hardware->h_ifname, protos[i].name,
-					    protocols[protos[i].mode]?"enabled":"disabled");
-			}
+	/* Print a debug message summarizing the operation */
+	i = j = 0;
+	TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+		if (((mask == SMART_EMISSION) && port->p_hidden_snd) ||
+		    ((mask == SMART_RECEPTION) && port->p_hidden_rcv))
+			i++;
+		j++;
+	}
+	if (i) {
+		LLOG_DEBUG("[%s] On %s, out of %d neighbors, %d are hidden",
+			   (mask == SMART_EMISSION)?"emission":"reception",
+			   hardware->h_ifname, j, i);
+		for (i=0; protos[i].mode != 0; i++) {
+			if (protos[i].enabled)
+				LLOG_DEBUG("[%s] On %s, %s is %s",
+					   (mask == SMART_EMISSION)?"emission":"reception",
+					   hardware->h_ifname, protos[i].name,
+					   protocols[protos[i].mode]?"enabled":"disabled");
 		}
 	}
 }
@@ -790,8 +811,8 @@ lldpd_send_all(struct lldpd *cfg)
 			TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
 				/* If this remote port is disabled, we don't
 				 * consider it */
-				if (port->p_hidden &&
-				    (cfg->g_smart & SMART_FILTER_EMISSION))
+				if (port->p_hidden_snd &&
+				    (cfg->g_smart & SMART_EMISSION_FILTER))
 					continue;
 				if (port->p_protocol ==
 				    cfg->g_protocols[i].mode) {
@@ -954,7 +975,7 @@ lldpd_loop(struct lldpd *cfg)
 	lldpd_update_localchassis(cfg);
 	lldpd_send_all(cfg);
 	lldpd_recv_all(cfg);
-	if (cfg->g_smart != SMART_NOFILTER)
+	if (cfg->g_smart)
 		lldpd_hide_all(cfg);
 }
 
@@ -985,6 +1006,42 @@ lldpd_exit()
 #endif /* USE_SNMP */
 }
 
+struct intint { int a; int b; };
+static const struct intint filters[] = {
+	{  0, 0 },
+	{  1, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO |
+	      SMART_EMISSION_FILTER | SMART_EMISSION_ONE_PROTO },
+	{  2, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO },
+	{  3, SMART_EMISSION_FILTER | SMART_EMISSION_ONE_PROTO },
+	{  4, SMART_RECEPTION_FILTER | SMART_EMISSION_FILTER },
+	{  5, SMART_RECEPTION_FILTER },
+	{  6, SMART_EMISSION_FILTER },
+	{  7, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO | SMART_RECEPTION_ONE_NEIGH |
+	      SMART_EMISSION_FILTER | SMART_EMISSION_ONE_PROTO },
+	{  8, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO | SMART_RECEPTION_ONE_NEIGH },
+	{  9, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH |
+	      SMART_EMISSION_FILTER | SMART_EMISSION_ONE_PROTO },
+	{ 10, SMART_EMISSION_FILTER | SMART_EMISSION_ONE_NEIGH },
+	{ 11, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH }
+	{ 12, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH |
+	      SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH }
+	{ 13, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH |
+	      SMART_RECEPTION_FILTER }
+	{ 14, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO |
+	      SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH }
+	{ 15, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO |
+	      SMART_RECEPTION_FILTER }
+	{ 16, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO | SMART_RECEPTION_ONE_NEIGH |
+	      SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH }
+	{ 17, SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO | SMART_RECEPTION_ONE_NEIGH |
+	      SMART_RECEPTION_FILTER }
+	{ 18, SMART_RECEPTION_FILTER |
+	      SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_NEIGH }
+	{ 19, SMART_RECEPTION_FILTER |
+	      SMART_RECEPTION_FILTER | SMART_RECEPTION_ONE_PROTO }
+	{ -1, 0 }
+};
+
 int
 lldpd_main(int argc, char *argv[])
 {
@@ -1004,7 +1061,7 @@ lldpd_main(int argc, char *argv[])
 #endif
         char *descr_override = NULL;
 	char *lsb_release = NULL;
-	int smart = SMART_FILTER_NO_TIE | SMART_FILTER_EMISSION | SMART_FILTER_RECEPTION;
+	int smart = 15;
 
 	saved_argv = argv;
 
@@ -1067,21 +1124,7 @@ lldpd_main(int argc, char *argv[])
                         descr_override = strdup(optarg);
                         break;
 		case 'H':
-			smart = SMART_NOFILTER;
-			i = atoi(optarg);
-			if (i == 0) break;
-			if ((i < 0) || (i > 9)) {
-				fprintf(stderr, "Incorrect mode for -H\n");
-				usage();
-			}
-			if (i%3 != 0)
-				smart |= SMART_FILTER_RECEPTION;
-			if ((i + 1)%3 != 0)
-				smart |= SMART_FILTER_EMISSION;
-			if (i > 6)
-				smart |= SMART_FILTER_ONE_NEIGH | SMART_FILTER_NO_TIE;
-			if (i < 4)
-				smart |= SMART_FILTER_NO_TIE;
+			smart = atoi(optarg);
 			break;
 		default:
 			found = 0;
@@ -1100,6 +1143,14 @@ lldpd_main(int argc, char *argv[])
 				usage();
 		}
 	}
+
+	/* Set correct smart mode */
+	for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
+	if (filters[i].a == -1) {
+		fprintf(stderr, "Incorrect mode for -H\n");
+		usage();
+	}
+	smart = filters[i].b;
 	
 	log_init(debug, __progname);
 	tzset();		/* Get timezone info before chroot */
diff --git a/src/lldpd.h b/src/lldpd.h
index 1de74a0..00ab26c 100644
--- a/src/lldpd.h
+++ b/src/lldpd.h
@@ -151,8 +151,8 @@ struct lldpd_port {
 	int			 p_id_len;
 	char			*p_descr;
 	u_int16_t		 p_mfs;
-	u_int8_t		 p_hidden; /* Hidden, this port information should
-					      be discarded if set to 1 */
+	u_int8_t		 p_hidden_rcv:1; /* Considered as hidden for reception */
+	u_int8_t		 p_hidden_snd:2; /* Considered as hidden for emission */
 
 #ifdef ENABLE_DOT3
 #define STRUCT_LLDPD_PORT_DOT3 "lbbww"
@@ -279,12 +279,19 @@ struct protocol {
 };
 
 /* Smart mode / Hide mode */
-#define SMART_NOFILTER		0
-#define SMART_FILTER_RECEPTION	(1<<0) /* Filter received frames */
-#define SMART_FILTER_EMISSION	(1<<1) /* Filter frames to be sent */
-#define SMART_FILTER_ONE_NEIGH	(1<<2) /* Only allow one neighbor */
-#define SMART_FILTER_NO_TIE	(1<<3) /* Only allow one protocol */
-#define SMART_HIDDEN(cfg, port) ((cfg->g_smart & SMART_FILTER_RECEPTION) && port->p_hidden)
+#define SMART_RECEPTION_FILTER		(1<<0) /* Reception filtering enabled */
+#define SMART_RECEPTION_ONE_PROTO	(1<<1) /* On reception, keep only one proto */
+#define SMART_RECEPTION_ONE_NEIGH	(1<<2) /* On reception, keep only one neighbor */
+#define SMART_ENISSION_FILTER		(1<<3) /* Emission filtering enabled */
+#define SMART_EMISSION_ONE_PROTO	(1<<4) /* On emission, keep only one proto */
+#define SMART_EMISSION_ONE_NEIGH	(1<<5) /* On emission, consider only one neighbor */
+#define SMART_RECEPTION (SMART_RECEPTION_FILTER |    \
+			 SMART_RECEPTION_ONE_PROTO | \
+			 SMART_RECEPTION_ONE_NEIGH)
+#define SMART_EMISSION (SMART_EMISSION_FILTER |		\
+			SMART_EMISSION_ONE_PROTO |	\
+			SMART_EMISSION_ONE_NEIGH)
+#define SMART_HIDDEN(cfg, port) ((cfg->g_smart & SMART_RECEPTION_FILTER) && port->p_hidden_rcv)
 
 
 #define CALLBACK_SIG struct lldpd*, struct lldpd_callback*

Reply via email to