Index: /branches/rel_apv_10_7_3/usr/click/bin/openssh/array.patch
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/bin/openssh/array.patch	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/bin/openssh/array.patch	(working copy)
@@ -90,7 +90,7 @@
  {
  	Authctxt *authctxt = ssh->authctxt;
  	struct passwd *pw = authctxt->pw;
-@@ -122,9 +137,41 @@ auth_password(struct ssh *ssh, const char *password)
+@@ -122,9 +137,44 @@ auth_password(struct ssh *ssh, const char *password)
  			authctxt->force_pwchange = 1;
  	}
  #endif
@@ -105,9 +105,9 @@
 +		if (getpwnam(user)) {
 +			local_exist = 1;
 +		}
-+
++		int only_aaa_failback = exauth_priority() == EXAUTH_PRIORITY_ONLY_AAA ? 0 : 1;
 +		/* do external auth if user not exist or exauth high priority */
-+		if (exauth_priority() == EXAUTH_PRIORITY_HIGH || !local_exist) {
++		if (exauth_priority() == EXAUTH_PRIORITY_HIGH || !local_exist || exauth_priority() == EXAUTH_PRIORITY_ONLY_AAA) {
 +			int ret_value = external_auth(user, password);
 +			if (ret_value == EXT_AUTH_PASS_CONFIG ) {
 +				ext_authenticated = 1;
@@ -117,13 +117,16 @@
 +				ext_authenticated = 1;
 +				ext_authorize_level = 0;
 +				result = 1;
++			} else if (ret_value == EXT_AUTH_UNAVAILABLE && exauth_priority() == EXAUTH_PRIORITY_ONLY_AAA) {
++				only_aaa_failback = 1;
++				result = 0;
 +			} else {
 +				result = 0;
 +			}
 +		}
 +
 +		/* check local database */
-+		if(local_exist && !ext_authenticated)
++		if(local_exist && !ext_authenticated && only_aaa_failback)
 +			result = sys_auth_passwd(ssh, password);
 +	/* admin aaa off */
 +	} else {
Index: /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext.h
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext.h	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext.h	(working copy)
@@ -36,6 +36,7 @@
 #define EXT_AUTH_FAILED			5
 #define EXT_AUTH_OTHER			6
 #define EXT_AUTH_PASS			7
+#define EXT_AUTH_UNAVAILABLE    8
 #define EXT_AUTHORIZE_OFF			0
 #define EXT_AUTHORIZE_ON			1
 
Index: /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext.c
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext.c	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext.c	(working copy)
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <radlib.h>
+#include <radlib_private.h>
 #include <taclib.h>
 #include <time.h>
 #include <stdarg.h>
@@ -158,6 +159,7 @@
     EXT_AUTH_FAILED
     EXT_AUTH_PASS_CONFIG
     EXT_AUTH_PASS_ENABLE
+    EXT_AUTH_UNAVAILABLE
 */
 /*authenticate with external server*/
 int
@@ -209,22 +211,22 @@
 external_auth_ldaps(char *username, char *password)
 {
     /*the return value of ldap response type*/
-    int ret_value = EXT_AUTH_FAILED;
-    int i;
+    int ret_value = EXT_AUTH_UNAVAILABLE;
+    int i = 3;
+
     if (username == NULL || password == NULL) {
         return EXT_AUTH_FAILED;
     }
+
     /*authenticate ldaps servers*/
-    for (i = 0; i < MAX_EXAU_SERVER_NUM; i++) {
-        if (g_exauth_conf.exauth_servers[i].id &&
-            g_exauth_conf.exauth_servers[i].id[0] != '\0') {
-            ret_value = external_auth_one_ldaps(i, username, password);
-            if ((ret_value == EXT_AUTH_PASS_CONFIG) ||
-                (ret_value == EXT_AUTH_PASS_ENABLE)) {
-                break;
-            }
-        }
-    }
+	if (
+		g_exauth_conf.exauth_servers[i].id &&
+		!strcmp(g_exauth_conf.exauth_servers[i].id, EXAUTH_SERVER_ID4)
+	)
+	{
+		ret_value = external_auth_one_ldaps(i, username, password);
+	}
+
     return ret_value;
 }
 
@@ -270,7 +272,7 @@
     int version = LDAP_VERSION3;
     ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
 
-    struct timeval timeout = {10, 0}; // 10 seconds timeout
+    struct timeval timeout = {5, 0}; // 5 seconds timeout
     // Set network timeout
     rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
     if (rc != LDAP_OPT_SUCCESS) {
@@ -330,13 +332,22 @@
     }
     msgid = ldap_simple_bind_s(ld, username, password);
 
-    if ( msgid != LDAP_SUCCESS ) {
-        exau_log("LDAPS login failed: %s for user %s.\n", ldap_err2string(msgid), username);
-        ldap_unbind_ext_s(ld, NULL, NULL);
-        return EXT_AUTH_FAILED;
-    } else {
-        exau_log("LDAPS bind successful for user %s.\n", username);
-        ret_value = EXT_AUTH_PASS_CONFIG;
+    switch (msgid) {
+        case LDAP_SUCCESS:
+            exau_log("LDAPS bind successful for user %s.\n", username);
+            ret_value = EXT_AUTH_PASS_CONFIG;
+            break;
+        case LDAP_SERVER_DOWN:
+        case LDAP_UNAVAILABLE:
+        case LDAP_TIMEOUT:
+        case LDAP_TIMELIMIT_EXCEEDED:
+        case LDAP_CONNECT_ERROR:
+            exau_log("LDAPS login unavailable for user %s. error: %s\n", username, ldap_err2string(msgid));
+            return EXT_AUTH_UNAVAILABLE;
+        default:
+            exau_log("LDAPS login failed for user %s. error: %s\n", username, ldap_err2string(msgid));
+            ldap_unbind_ext_s(ld, NULL, NULL);
+            return EXT_AUTH_FAILED;
     }
 
     // authorization off then will not do authorization only authentication
@@ -355,12 +366,22 @@
     }
     msgid = ldap_search_s(ld, g_exauth_conf.exauth_servers[i].dn, LDAP_SCOPE_SUBTREE, search_string,
                          attrs, attrsonly, &answer);
-    if ( msgid != LDAP_SUCCESS ) {
-        exau_log("LDAPS search failed: %s for user %s.\n", ldap_err2string(msgid), username);
-        ldap_unbind_ext_s(ld, NULL, NULL);
-        return EXT_AUTH_FAILED;
-    } else {
-        exau_log("LDAPS search successful for user %s.\n", username);
+    switch (msgid) {
+        case LDAP_SUCCESS:
+            exau_log("LDAPS search successful for user %s.\n", username);
+            break;
+        case LDAP_SERVER_DOWN:
+        case LDAP_UNAVAILABLE:
+        case LDAP_TIMEOUT:
+        case LDAP_TIMELIMIT_EXCEEDED:
+        case LDAP_CONNECT_ERROR:
+            exau_log("LDAPS search unavailable for user %s. error: %s\n", username, ldap_err2string(msgid));
+            ldap_unbind_ext_s(ld, NULL, NULL);
+            return EXT_AUTH_UNAVAILABLE;
+        default:
+            exau_log("LDAPS search failed for user %s. error: %s\n", username, ldap_err2string(msgid));
+            ldap_unbind_ext_s(ld, NULL, NULL);
+            return EXT_AUTH_FAILED;
     }
 
     /* Return the number of objects found during the search */
@@ -389,22 +410,22 @@
 external_auth_ldap(char *username, char *password)
 {
     /*the return value of ldap response type*/
-    int ret_value = EXT_AUTH_FAILED;
-    int i;
+    int ret_value = EXT_AUTH_UNAVAILABLE;
+    int i = 2;
+
     if (username == NULL || password == NULL) {
         return EXT_AUTH_FAILED;
     }
+
     /*authenticate ldap servers*/
-    for (i = 0; i < MAX_EXAU_SERVER_NUM; i++) {
-        if (g_exauth_conf.exauth_servers[i].id &&
-            g_exauth_conf.exauth_servers[i].id[0] != '\0') {
-            ret_value = external_auth_one_ldap(i, username, password);
-            if ((ret_value == EXT_AUTH_PASS_CONFIG) ||
-                (ret_value == EXT_AUTH_PASS_ENABLE)) {
-                break;
-            }
-        }
-    }
+	if (
+		g_exauth_conf.exauth_servers[i].id &&
+		!strcmp(g_exauth_conf.exauth_servers[i].id, EXAUTH_SERVER_ID3)
+	)
+	{
+		ret_value = external_auth_one_ldap(i, username, password);
+	}
+
     return ret_value;
 }
 int
@@ -414,7 +435,7 @@
     struct timeval to;
     int ldap_version = LDAP_VERSION3;
     int msgid = 0;
-    to.tv_sec = 600;
+    to.tv_sec = 5;
     to.tv_usec = 0;
     int ret_value = EXT_AUTH_FAILED;
     int i = server_num;
@@ -453,12 +474,21 @@
     }
     msgid = ldap_simple_bind_s(ldap, username, password);
 
-    if ( msgid != LDAP_SUCCESS ) {
-        exau_log("LDAP login failed for user %s.\n", username);
-        return EXT_AUTH_FAILED;
-    } else {
-        exau_log("LDAP login successful for user %s.\n", username);
-        ret_value = EXT_AUTH_PASS_CONFIG;
+    switch (msgid) {
+        case LDAP_SUCCESS:
+            exau_log("LDAP login successful for user %s.\n", username);
+            ret_value = EXT_AUTH_PASS_CONFIG;
+            break;
+        case LDAP_SERVER_DOWN:
+        case LDAP_UNAVAILABLE:
+        case LDAP_TIMEOUT:
+        case LDAP_TIMELIMIT_EXCEEDED:
+        case LDAP_CONNECT_ERROR:
+            exau_log("LDAP login unavailable for user %s. error: %s\n", username, ldap_err2string(msgid));
+            return EXT_AUTH_UNAVAILABLE;
+        default:
+            exau_log("LDAP login failed for user %s. error: %s\n", username, ldap_err2string(msgid));
+            return EXT_AUTH_FAILED;
     }
     // authorization off then will not do authorization only authentication
     if (g_exauth_conf.external_authorize_on == EXT_AUTHORIZE_OFF){
@@ -478,11 +508,20 @@
     msgid = ldap_search_s(ldap, g_exauth_conf.exauth_servers[i].dn, LDAP_SCOPE_SUBTREE, search_string,
                          attrs, attrsonly, &answer);
 
-    if ( msgid != LDAP_SUCCESS ) {
-        exau_log("LDAP search failed for user %s.\n", username);
-        return EXT_AUTH_FAILED;
-    } else {
-        exau_log("LDAP search successful for user %s.\n", username);
+    switch (msgid) {
+        case LDAP_SUCCESS:
+            exau_log("LDAP search successful for user %s.\n", username);
+            break;
+        case LDAP_SERVER_DOWN:
+        case LDAP_UNAVAILABLE:
+        case LDAP_TIMEOUT:
+        case LDAP_TIMELIMIT_EXCEEDED:
+        case LDAP_CONNECT_ERROR:
+            exau_log("LDAP search unavailable for user %s. error: %s\n", username, ldap_err2string(msgid));
+            return EXT_AUTH_UNAVAILABLE;
+        default:
+            exau_log("LDAP search failed for user %s. error: %s\n", username, ldap_err2string(msgid));
+            return EXT_AUTH_FAILED;
     }
 
     /* Return the number of objects found during the search */
@@ -503,6 +542,25 @@
     return ret_value;
 }
 
+int
+rad_classify_error(struct rad_handle *h)
+{
+    const char *err_msg = rad_strerror(h);
+    exau_log("rad_classify_error: err_msg=%s\n", err_msg);
+    if (
+        strstr(err_msg, "Cannot create socket") ||
+        strstr(err_msg, "bind") ||
+        strstr(err_msg, "recvfrom") ||
+        strstr(err_msg, "No valid RADIUS responses received") ||
+        strstr(err_msg, "rad_init_send_request: have no server find") ||
+        strstr(err_msg, "select")
+    ) {
+        return EXT_AUTH_UNAVAILABLE;
+    }
+
+    return EXT_AUTH_FAILED;
+}
+
 /*authenticate with external radius server*/
 /*To support ipv6 auth server, we modified the libradius library to support ipv6 too.
   This library can handle up to 10 auth servers in one auth request, when we originate
@@ -633,7 +691,7 @@
 		break;
 
 	default:
-		ret_value = EXT_AUTH_FAILED;
+		ret_value = rad_classify_error(rh);
 	}
 	rad_close(rh);
 
@@ -645,24 +703,20 @@
 external_auth_radius(char *username, char *password)
 {
 	/*the return value of radius response type*/
-	int ret_value = EXT_AUTH_FAILED;
-	int i;
+	int ret_value = EXT_AUTH_UNAVAILABLE;
+	int i = 0;
 
 	if (username == NULL || password == NULL) {
 		return EXT_AUTH_FAILED;
 	}
 
-
 	/*authenticate radius servers*/
-	for (i = 0; i < MAX_EXAU_SERVER_NUM; i++) {
-		if (g_exauth_conf.exauth_servers[i].id &&
-		    g_exauth_conf.exauth_servers[i].id[0] != '\0') {
-			ret_value = external_auth_one_radius(i, username, password);
-			if ((ret_value == EXT_AUTH_PASS_CONFIG) ||
-			    (ret_value == EXT_AUTH_PASS_ENABLE)) {
-				break;
-			}
-		}
+	if (
+		g_exauth_conf.exauth_servers[i].id &&
+		!strcmp(g_exauth_conf.exauth_servers[i].id, EXAUTH_SERVER_ID1)
+	)
+	{
+		ret_value = external_auth_one_radius(i, username, password);
 	}
 
 	return ret_value;
@@ -854,6 +908,30 @@
 	return 0;
 }
 
+int
+tac_classify_error(struct tac_handle *th, int ret_real_type)
+{
+	if (ret_real_type != -1) {
+		return EXT_AUTH_FAILED;
+	}
+
+	const char *err_msg = tac_strerror(th);
+
+	if (
+		strstr(err_msg, "Network write timed out") ||
+		strstr(err_msg, "Network write error") ||
+		strstr(err_msg, "No TACACS+ servers available") ||
+		strstr(err_msg, "select") ||
+		strstr(err_msg, "Network read error") ||
+		strstr(err_msg, "Network read timed out") ||
+		strstr(err_msg, "unexpected EOF from server")
+	) {
+		return EXT_AUTH_UNAVAILABLE;
+	}
+
+	return EXT_AUTH_FAILED;
+}
+
 /*
  * There seems a problem in tac_send_authen() of libtacplus
  * for it mistakenly implemented the "secret" assigned to the 1st server
@@ -870,6 +948,7 @@
 	char msg[256];
 
 	/*the return value of tac_x response type*/
+	int ret_real_type = 0;
 	int ret_type = 0;
 	int ret_value = EXT_AUTH_FAILED;
 
@@ -928,16 +1007,18 @@
 	}
 
 	/*send the START packet and check the response*/
-	ret_type = TAC_AUTHEN_STATUS(tac_send_authen(th));
+	ret_real_type = tac_send_authen(th);
+	ret_type = TAC_AUTHEN_STATUS(ret_real_type);
 
 	/*get the ret_type string*/
 	check_tac_x_type(ret_type, msg);
 
 	/*expected the response should be TAC_AUTHEN_STATUS_GETPASS*/
 	if (ret_type != TAC_AUTHEN_STATUS_GETPASS) {
+		ret_value = tac_classify_error(th, ret_real_type);
 		tac_close(th);
 		exau_log("auth_one_tacx_server(): expect TAC_AUTHEN_STATUS_GETPASS, but received %s\n", msg);
-		return EXT_AUTH_FAILED;
+		return ret_value;
 	}
 
 	/*attache the PASSWD to the CONTINUE packet*/
@@ -948,12 +1029,13 @@
 	}
 
 	/*send the CONTINUED packet and check the response*/
-	ret_type = TAC_AUTHEN_STATUS(tac_send_authen(th));
+	ret_real_type = tac_send_authen(th);
+	ret_type = TAC_AUTHEN_STATUS(ret_real_type);
 
 	/*get the ret_type string*/
 	check_tac_x_type(ret_type, msg);
 	if (ret_type != TAC_AUTHEN_STATUS_PASS) {
-		ret_value = EXT_AUTH_FAILED;
+		ret_value = tac_classify_error(th, ret_real_type);
 		tac_close(th);
 		exau_log("auth_one_tacx_server(): EXT_AUTH_FAILED\n");
 		return ret_value;
@@ -989,7 +1071,8 @@
 		return EXT_AUTH_FAILED;
 	}
 
-	ret_type = TAC_AUTHOR_STATUS(tac_send_author(th));
+	ret_real_type = tac_send_author(th);
+	ret_type = TAC_AUTHOR_STATUS(ret_real_type);
 	if (ret_type == TAC_AUTHOR_STATUS_PASS_ADD) {
 		ret_level = tac_get_av_value(th, "priv-lvl");
 		if (ret_level && !strncmp(ret_level, "15", 2)) {
@@ -1008,24 +1091,20 @@
 external_auth_tacx(char *username, char *password)
 {
 	/*the return value of  tac_x response type*/
-	int ret_value = EXT_AUTH_FAILED;
-	int i;
+	int ret_value = EXT_AUTH_UNAVAILABLE;
+	int i = 1;
 
 	if (username == NULL || password == NULL) {
 		return EXT_AUTH_FAILED;
 	}
 
-
 	/*authenticate tac_x servers*/
-	for (i = 0; i < MAX_EXAU_SERVER_NUM; i++) {
-		if (g_exauth_conf.exauth_servers[i].id &&
-		    g_exauth_conf.exauth_servers[i].id[0] != '\0') {
-			ret_value = auth_one_tacx_server(i, username, password);
-			if ((ret_value == EXT_AUTH_PASS_CONFIG) ||
-			    (ret_value == EXT_AUTH_PASS_ENABLE)) {
-				break;
-			}
-		}
+	if (
+		g_exauth_conf.exauth_servers[i].id &&
+		!strcmp(g_exauth_conf.exauth_servers[i].id, EXAUTH_SERVER_ID2)
+	)
+	{
+		ret_value = auth_one_tacx_server(i, username, password);
 	}
 
 	return ret_value;
@@ -1248,4 +1327,4 @@
 	}
 
 	return method;
-}
\ No newline at end of file
+}
Index: /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext_cli.h
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext_cli.h	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext_cli.h	(working copy)
@@ -51,6 +51,7 @@
 
 #define EXAUTH_PRIORITY_HIGH		1
 #define EXAUTH_PRIORITY_LOW 		0
+#define EXAUTH_PRIORITY_ONLY_AAA	2
 
 /*authentication method table*/
 typedef enum exau_method {
Index: /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext_cli.c
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext_cli.c	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/lib/libexauth/auth_ext_cli.c	(working copy)
@@ -75,8 +75,8 @@
 int 
 exauth_on(int priority)
 {
-	if (priority != 0 && priority != 1){
-		printf("Priority should be 0 or 1\n");
+	if (priority != 0 && priority != 1 && priority != 2){
+		printf("Priority should be 0, 1 or 2\n");
 		return -1;
 	}
 	
Index: /branches/rel_apv_10_7_3/usr/click/lib/libexauth/taclib.c
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/lib/libexauth/taclib.c	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/lib/libexauth/taclib.c	(working copy)
@@ -399,6 +399,7 @@
 			h->cur_server = 0;
 	}
 	/* Just return whatever error was last reported by conn_server(). */
+	generr(h, "No TACACS+ servers available");
 	return -1;
 }
 
Index: /branches/rel_apv_10_7_3/usr/click/lib/libparser/commands.pm
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/lib/libparser/commands.pm	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/lib/libparser/commands.pm	(working copy)
@@ -50481,7 +50481,7 @@
 		function_args => [
 							{
 								type => "U32",
-								help_string => "Priority. 0: local database first; 1: external AAA server first. (Default = 0)",
+								help_string => "Priority. 0: local database first; 1: external AAA server first; 2: external AAA server only. If it is unavailable, fallback to local database. (Default = 0)",
 								optional => "YES",
 								default_value => 0,
 							},
Index: /branches/rel_apv_10_7_3/usr/click/lib/libpyauth/pyauth.c
===================================================================
--- /branches/rel_apv_10_7_3/usr/click/lib/libpyauth/pyauth.c	(revision 40109)
+++ /branches/rel_apv_10_7_3/usr/click/lib/libpyauth/pyauth.c	(working copy)
@@ -24,6 +24,7 @@
 #ifndef EXAUTH_PRIORITY_HIGH
 #define EXAUTH_PRIORITY_HIGH		1
 #define EXAUTH_PRIORITY_LOW 		0
+#define EXAUTH_PRIORITY_ONLY_AAA	2
 #endif
 
 #define BASE_AUTH_FAILED 100
@@ -411,7 +412,9 @@
 
     
     if (is_external_auth_on()) {
-        if (exauth_priority() == EXAUTH_PRIORITY_HIGH) {
+        if (
+            exauth_priority() == EXAUTH_PRIORITY_HIGH ||
+            exauth_priority() == EXAUTH_PRIORITY_ONLY_AAA) {
             ret = external_auth(username, password);
         } else {
             ret = local_auth(username, password, api);
@@ -420,10 +423,16 @@
             unlock_user(user_node_idx);
             login_info_shm_detach();
             return ret;
+        } else if (
+            exauth_priority() == EXAUTH_PRIORITY_ONLY_AAA &&
+            ret != EXT_AUTH_UNAVAILABLE) {
+            return ret;
         }
 	
 	/*Priority authentication failed, recertification*/
-        if (exauth_priority() == EXAUTH_PRIORITY_HIGH) {
+        if (
+            exauth_priority() == EXAUTH_PRIORITY_HIGH ||
+            exauth_priority() == EXAUTH_PRIORITY_ONLY_AAA) {
             ret2 = local_auth(username, password, api);
         } else {
             ret2 = external_auth(username, password);
