示例#1
0
static void dump_rules(ruletree_object_offset_t offs, int indent)
{
	ruletree_fsrule_t	*rule = offset_to_ruletree_fsrule_ptr(offs);
	const char *rule_list_link_label = "??";

	if (rule_dumped[offs]) {
		print_indent(indent + 1);
		printf("[ => @ %u]\n", offs);
		return;
	}
	rule_dumped[offs] = 1;

	print_indent(indent);
	printf("{ Rule[%u]:\n", (unsigned)offs);

	if (rule->rtree_fsr_name_offs) {
		print_indent(indent+1);
		printf("name = '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_name_offs, NULL));
	}

	if (rule->rtree_fsr_selector_type) {
		print_indent(indent+1);
		printf("IF: ");
		switch (rule->rtree_fsr_selector_type) {
		case SB2_RULETREE_FSRULE_SELECTOR_PREFIX:
			printf("prefix '%s'\n",
				offset_to_ruletree_string_ptr(rule->rtree_fsr_selector_offs, NULL));
			break;
		case SB2_RULETREE_FSRULE_SELECTOR_DIR:
			printf("dir '%s'\n",
				offset_to_ruletree_string_ptr(rule->rtree_fsr_selector_offs, NULL));
			break;
		case SB2_RULETREE_FSRULE_SELECTOR_PATH:
			printf("path '%s'\n",
				offset_to_ruletree_string_ptr(rule->rtree_fsr_selector_offs, NULL));
			break;
		default:
			printf("ERROR: Unknown selector type %d\n",
				rule->rtree_fsr_selector_type);
			break;
		}
	}

	if (rule->rtree_fsr_condition_type) {
		const char *condstr = offset_to_ruletree_string_ptr(rule->rtree_fsr_condition_offs, NULL);

		print_indent(indent+1);
		printf("CONDITIONAL: ");
		switch (rule->rtree_fsr_condition_type) {
		case SB2_RULETREE_FSRULE_CONDITION_IF_ACTIVE_EXEC_POLICY_IS:
			printf("if_active_exec_policy_is '%s'\n", condstr);
			break;
		case SB2_RULETREE_FSRULE_CONDITION_IF_REDIRECT_IGNORE_IS_ACTIVE:
			printf("if_redirect_ignore_is_active '%s'\n", condstr);
			break;
		case SB2_RULETREE_FSRULE_CONDITION_IF_REDIRECT_FORCE_IS_ACTIVE:
			printf("if_redirect_force_is_active '%s'\n", condstr);
			break;
		case SB2_RULETREE_FSRULE_CONDITION_IF_ENV_VAR_IS_NOT_EMPTY:
			printf("if_env_var_is_not_empty '%s'\n", condstr);
			break;
		case SB2_RULETREE_FSRULE_CONDITION_IF_ENV_VAR_IS_EMPTY:
			printf("if_env_var_is_empty '%s'\n", condstr);
			break;
		default:
			printf("ERROR: Unknown condition type %d\n",
				rule->rtree_fsr_condition_type);
			break;
		}
	}

	if (rule->rtree_fsr_func_class) {
		print_indent(indent+1);
		printf("IF_CLASS: 0x%X ( ", rule->rtree_fsr_func_class);
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_OPEN)
		       printf("open ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_STAT)
		       printf("stat ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_EXEC)
		       printf("exec ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_SOCKADDR)
		       printf("sockaddr ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_FTSOPEN)
		       printf("ftsopen ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_GLOB)
		       printf("glob ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_GETCWD)
		       printf("getcwd ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_REALPATH)
		       printf("realpath ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_L10N)
		       printf("l10n ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_MKNOD)
		       printf("mknod ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_RENAME)
		       printf("rename ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_SYMLINK)
		       printf("symlink ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_CREAT)
		       printf("creat ");
		if (rule->rtree_fsr_func_class & SB2_INTERFACE_CLASS_PROC_FS_OP)
		       printf("proc_fs_op ");
		printf(")\n");
	}
	
	if (rule->rtree_fsr_binary_name) {
		const char *bin_name = offset_to_ruletree_string_ptr(rule->rtree_fsr_binary_name, NULL);
		print_indent(indent+1);
		printf("BINARY_NAME: '%s'\n", bin_name);
	}

	if (rule->rtree_fsr_exec_policy_name) {
		const char *ep_name = offset_to_ruletree_string_ptr(rule->rtree_fsr_exec_policy_name, NULL);
		print_indent(indent+1);
		printf("EXEC_POLICY_NAME: '%s'\n", ep_name);
	}

	print_indent(indent+1);
	printf("ACTION: ");
	switch (rule->rtree_fsr_action_type) {
	case SB2_RULETREE_FSRULE_ACTION_FALLBACK_TO_OLD_MAPPING_ENGINE:
		printf("FALLBACK_TO_OLD_MAPPING_ENGINE\n");
		break;	
	case SB2_RULETREE_FSRULE_ACTION_PROCFS:
		printf("sb2_procfs_mapper\n");
		break;	
	case SB2_RULETREE_FSRULE_ACTION_UNION_DIR:
		printf("union_dir => rule->rtree_fsr_rule_list_link\n");
		rule_list_link_label = "union_dir";
		break;	
	case SB2_RULETREE_FSRULE_ACTION_USE_ORIG_PATH:
		printf("use_orig_path\n");
		break;	
	case SB2_RULETREE_FSRULE_ACTION_FORCE_ORIG_PATH:
		printf("force_orig_path\n");
		break;	
	case SB2_RULETREE_FSRULE_ACTION_REPLACE_BY:
		printf("replace_by '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_action_offs, NULL));
		break;
	case SB2_RULETREE_FSRULE_ACTION_SET_PATH:
		printf("set_path '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_action_offs, NULL));
		break;
	case SB2_RULETREE_FSRULE_ACTION_MAP_TO:
		printf("map_to '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_action_offs, NULL));
		break;
	case SB2_RULETREE_FSRULE_ACTION_REPLACE_BY_VALUE_OF_ENV_VAR:
		printf("replace_by_value_of_env_var '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_action_offs, NULL));
		break;
	case SB2_RULETREE_FSRULE_ACTION_MAP_TO_VALUE_OF_ENV_VAR:
		printf("map_to_value_of_env_var '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_action_offs, NULL));
		break;
	case SB2_RULETREE_FSRULE_ACTION_CONDITIONAL_ACTIONS:
		printf("actions => %d\n",
			rule->rtree_fsr_rule_list_link);
		rule_list_link_label = "actions";
		break;
	case SB2_RULETREE_FSRULE_ACTION_SUBTREE:
		printf("subtree => %d\n",
			rule->rtree_fsr_rule_list_link);
		rule_list_link_label = "rules";
		break;
	case SB2_RULETREE_FSRULE_ACTION_IF_EXISTS_THEN_MAP_TO:
		printf("if_exists_then_map_to '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_action_offs, NULL));
		break;
	case SB2_RULETREE_FSRULE_ACTION_IF_EXISTS_THEN_REPLACE_BY:
		printf("if_exists_then_replace_by '%s'\n",
			offset_to_ruletree_string_ptr(rule->rtree_fsr_action_offs, NULL));
		break;
	default:
		printf("ERROR: Unknown action type %d\n",
			rule->rtree_fsr_action_type);
		break;
	}

	if (rule->rtree_fsr_rule_list_link) {
		print_indent(indent+1);
		printf("%s = {\n", rule_list_link_label);
		dump_objectlist(rule->rtree_fsr_rule_list_link, indent + 2);
		print_indent(indent+1);
		printf("}\n");
	}
	print_indent(indent);
	printf("}\n");
}
示例#2
0
static char *ruletree_execute_conditional_actions(
        const path_mapping_context_t *ctx,
        int result_log_level,
        const char *abs_clean_virtual_path,
        int *flagsp,
        const char **exec_policy_name_ptr,
	const char **errormsgp,
	ruletree_fsrule_t	*rule_selector)
{
	uint32_t	actions_list_size;
	uint32_t	i;
	ruletree_object_offset_t action_list_offs = rule_selector->rtree_fsr_rule_list_link;

	/* FIXME: these are not yet used. */
	(void)ctx;
	(void)result_log_level;
        (void)exec_policy_name_ptr;

	LB_LOG(LB_LOGLEVEL_NOISE, "ruletree_execute_conditional_actions for (%s)", abs_clean_virtual_path);
	actions_list_size = ruletree_objectlist_get_list_size(action_list_offs);
	if (actions_list_size == 0) {
		LB_LOG(LB_LOGLEVEL_DEBUG,
			"ruletree_execute_conditional_actions: action list not found or empty.");
		*errormsgp = "action list not found or empty.";
		return (NULL);
	}

	for (i = 0; i < actions_list_size; i++) {
		ruletree_fsrule_t	*action_cand_p;
		ruletree_object_offset_t action_offs;

		action_offs = ruletree_objectlist_get_item(action_list_offs, i);
		if (!action_offs) continue;

		/* "rule_selector" is the rule which matched, and
		 * brought us here (so it contains "dir","prefix"
		 * or "path").
		 * Each member in the "actions" array is a 
                 * candidate for the rule which will be applied,
		 * i.e. a suitable member from that array gives
		 * instructions about what to do next */
		action_cand_p = offset_to_ruletree_fsrule_ptr(action_offs);
		if (action_cand_p) {
			char *mapping_result;

			if (action_cand_p->rtree_fsr_condition_type != 0) {
				const char *cond_str;
				const char *evp;

				cond_str = offset_to_ruletree_string_ptr(action_cand_p->rtree_fsr_condition_offs, NULL);
				LB_LOG(LB_LOGLEVEL_NOISE, "Condition test '%s'", cond_str);
				
				switch (action_cand_p->rtree_fsr_condition_type) {
				case LB_RULETREE_FSRULE_CONDITION_IF_ENV_VAR_IS_NOT_EMPTY:
					if (!cond_str) continue;	/* continue if no env.var.name */
					evp = getenv(cond_str);
					if (!evp || !*evp) continue; /* continue if empty */
					LB_LOG(LB_LOGLEVEL_NOISE, "Condition test: env.var was not empty");
					break;	/* else test passed. */
					
				case LB_RULETREE_FSRULE_CONDITION_IF_ENV_VAR_IS_EMPTY:
					if (!cond_str) continue;	/* continue if no env.var.name */
					evp = getenv(cond_str);
					if (evp && *evp) continue; /* continue if not empty */
					LB_LOG(LB_LOGLEVEL_NOISE, "Condition test: env.var was empty");
					break;	/* else test passed. */
				
				case LB_RULETREE_FSRULE_CONDITION_IF_ACTIVE_EXEC_POLICY_IS:
					if (!cond_str ||
					    !ldbox_active_exec_policy_name ||
					    strcmp(cond_str, ldbox_active_exec_policy_name)) {
						/* exec policy name did not match */
						continue;
					}
					LB_LOG(LB_LOGLEVEL_NOISE, "Condition test: exec policy name matched");
					break;

				case LB_RULETREE_FSRULE_CONDITION_IF_REDIRECT_IGNORE_IS_ACTIVE:
					if (!cond_str) continue;	/* continue if no path */
					if (test_if_str_in_colon_separated_list_from_env(
						cond_str, "LDBOX_REDIRECT_IGNORE")) {
						LB_LOG(LB_LOGLEVEL_NOISE, "Condition test: redirect-ignore is active (%s)",
							cond_str);
					} else {
						LB_LOG(LB_LOGLEVEL_NOISE, "Condition test: redirect-ignore is NOT active (%s)",
							cond_str);
						continue;
					}
					break;

				case LB_RULETREE_FSRULE_CONDITION_IF_REDIRECT_FORCE_IS_ACTIVE:
					if (!cond_str) continue;	/* continue if no path */
					if (test_if_str_in_colon_separated_list_from_env(
						cond_str, "LDBOX_REDIRECT_FORCE")) {
						LB_LOG(LB_LOGLEVEL_NOISE, "Condition test: redirect-force is active (%s)",
							cond_str);
					} else {
						LB_LOG(LB_LOGLEVEL_NOISE, "Condition test: redirect-force is NOT active (%s)",
							cond_str);
						continue;
					}
					break;

                                case LB_RULETREE_FSRULE_CONDITION_IF_EXISTS_IN:
                                  if (if_exists_in(action_cand_p, abs_clean_virtual_path)) {
                                    /* found, jump to the new rule tree branch */
                                    ruletree_object_offset_t then_actions_offset = action_cand_p->rtree_fsr_rule_list_link;
                                    if (!then_actions_offset) {
                                      LB_LOG(LB_LOGLEVEL_DEBUG, "if_exists_in: no then_actions found");
                                      /* continue with normal rule tree */
                                      continue;
                                    }
                                    else {
                                      LB_LOG(LB_LOGLEVEL_DEBUG, "if_exists_in: then_actions found (%d)", then_actions_offset);
                                      ruletree_fsrule_t new_rules = *rule_selector;
                                      new_rules.rtree_fsr_rule_list_link = then_actions_offset;
                                      return ruletree_execute_conditional_actions(ctx, result_log_level,
                                                                                  abs_clean_virtual_path, flagsp,
                                                                                  exec_policy_name_ptr, errormsgp,
                                                                                  &new_rules);
                                    }
                                  }
                                  /* not found, continue with normal rule tree */
                                  continue;
                                  break;

				default:
					LB_LOG(LB_LOGLEVEL_ERROR,
						"ruletree_execute_conditional_actions: "
						" unknown condition %d @%d",
						action_cand_p->rtree_fsr_condition_type, action_offs);
					goto unimplemented_action_error;
				}
			}

			switch (action_cand_p->rtree_fsr_action_type) {
			case LB_RULETREE_FSRULE_ACTION_IF_EXISTS_THEN_MAP_TO:
				if (if_exists_then_map_to(action_cand_p,
				     abs_clean_virtual_path, &mapping_result)) {
					return(mapping_result);
				}
				break;

			case LB_RULETREE_FSRULE_ACTION_IF_EXISTS_THEN_REPLACE_BY:
				if (if_exists_then_replace_by(action_cand_p,
				     rule_selector, abs_clean_virtual_path,
				     &mapping_result)) {
					return(mapping_result);
				}
				break;

			case LB_RULETREE_FSRULE_ACTION_USE_ORIG_PATH:
			case LB_RULETREE_FSRULE_ACTION_FORCE_ORIG_PATH:
			case LB_RULETREE_FSRULE_ACTION_FORCE_ORIG_PATH_UNLESS_CHROOT:
			case LB_RULETREE_FSRULE_ACTION_MAP_TO:
			case LB_RULETREE_FSRULE_ACTION_REPLACE_BY:
			case LB_RULETREE_FSRULE_ACTION_SET_PATH:
			case LB_RULETREE_FSRULE_ACTION_MAP_TO_VALUE_OF_ENV_VAR:
			case LB_RULETREE_FSRULE_ACTION_REPLACE_BY_VALUE_OF_ENV_VAR:
			case LB_RULETREE_FSRULE_ACTION_PROCFS:
			case LB_RULETREE_FSRULE_ACTION_UNION_DIR:
				return(execute_std_action(rule_selector, action_cand_p,
					abs_clean_virtual_path, flagsp));

			default:
				/* FIXME */
				goto unimplemented_action_error;
			}
		}
	}
	/* end of list is most probably a fatal error in the rule file. */
	LB_LOG(LB_LOGLEVEL_ERROR,
		"ruletree_execute_conditional_actions: End of conditional action list, "
		"probably caused by an error in the rule file.");
	/* FIXME. This should probably return the original path (compare with
	 * Lua code) */
	*errormsgp = "End of conditional action list";
	return (NULL);
			
    unimplemented_action_error:
	LB_LOG(LB_LOGLEVEL_ERROR,
		"Internal error: ruletree_execute_conditional_actions: Encountered "
		"an unknown conditional action.");
	*errormsgp = "unknown conditional action (internal error)";
	return (NULL);
}
示例#3
0
char *ruletree_translate_path(
        const path_mapping_context_t *ctx,
        int result_log_level,
        const char *abs_clean_virtual_path,
        int *flagsp,
        const char **exec_policy_name_ptr,
	const char **errormsgp)
{
	char	*host_path = NULL;
	ruletree_fsrule_t *rule;
	PROCESSCLOCK(clk1)

	START_PROCESSCLOCK(LB_LOGLEVEL_INFO, &clk1, "ruletree_translate_path");
	*errormsgp = NULL;

	if (!ctx->pmc_ruletree_offset) {
		/* This might happen during initialization phase,
		 * when rule tree is not yet available */
		LB_LOG(LB_LOGLEVEL_DEBUG, "ruletree_translate_path: No rule tree");
		*errormsgp = "No rule tree";
		return(NULL);
	}
	rule = offset_to_ruletree_fsrule_ptr(ctx->pmc_ruletree_offset);

	LB_LOG(LB_LOGLEVEL_DEBUG, "ruletree_translate_path(%s)",
		abs_clean_virtual_path);

	/* execute rule */
	/* FIXME: should care about the R/O flag... */

	if (flagsp) *flagsp = rule->rtree_fsr_flags;
	if (exec_policy_name_ptr) {
		if (rule->rtree_fsr_exec_policy_name) {
			*exec_policy_name_ptr = offset_to_ruletree_string_ptr(rule->rtree_fsr_exec_policy_name, NULL);
		} else {
			*exec_policy_name_ptr = NULL;
		}
	}

	switch (rule->rtree_fsr_action_type) {
	case LB_RULETREE_FSRULE_ACTION_FALLBACK_TO_OLD_MAPPING_ENGINE:
		LB_LOG(LB_LOGLEVEL_WARNING,
			"ruletree_translate_path: Forced fallback. This should never happen nowadays.");
		host_path = NULL;
		break;

	case LB_RULETREE_FSRULE_ACTION_USE_ORIG_PATH:
	case LB_RULETREE_FSRULE_ACTION_FORCE_ORIG_PATH:
	case LB_RULETREE_FSRULE_ACTION_FORCE_ORIG_PATH_UNLESS_CHROOT:
	case LB_RULETREE_FSRULE_ACTION_MAP_TO:
	case LB_RULETREE_FSRULE_ACTION_REPLACE_BY:
	case LB_RULETREE_FSRULE_ACTION_SET_PATH:
	case LB_RULETREE_FSRULE_ACTION_MAP_TO_VALUE_OF_ENV_VAR:
	case LB_RULETREE_FSRULE_ACTION_REPLACE_BY_VALUE_OF_ENV_VAR:
	case LB_RULETREE_FSRULE_ACTION_PROCFS:
	case LB_RULETREE_FSRULE_ACTION_UNION_DIR:
		host_path = execute_std_action(rule, rule, abs_clean_virtual_path, flagsp);
		break;

	case LB_RULETREE_FSRULE_ACTION_CONDITIONAL_ACTIONS:
		host_path = ruletree_execute_conditional_actions(
			ctx, result_log_level, abs_clean_virtual_path,
			flagsp, exec_policy_name_ptr, errormsgp,
			rule);
		break;
	
	default:
		LB_LOG(LB_LOGLEVEL_ERROR,
			"ruletree_translate_path: Unknown action code %d",
			rule->rtree_fsr_action_type);
		host_path = NULL;
		break;
	}
	if (host_path) {
		if (*host_path != '/') {
			LB_LOG(LB_LOGLEVEL_ERROR,
				"Mapping failed: Result is not absolute ('%s'->'%s')",
				abs_clean_virtual_path, host_path);
			host_path = NULL;
		} else {
			char *new_host_path = clean_and_log_fs_mapping_result(ctx,
				abs_clean_virtual_path, result_log_level, host_path, *flagsp);
			if (new_host_path == NULL) {
				LB_LOG(result_log_level,
					"Mapping failed ('%s')",
					abs_clean_virtual_path);
				*errormsgp = "Mapping failed";
			}
			free(host_path);
			host_path = new_host_path;
		}
	} else {
		LB_LOG(result_log_level,
			"Mapping failed: Fallback to Lua mapping ('%s')",
			abs_clean_virtual_path);
		*errormsgp = "No result from C mapping";
	}
	STOP_AND_REPORT_PROCESSCLOCK(LB_LOGLEVEL_INFO, &clk1, host_path);
	return(host_path);
}
示例#4
0
static ruletree_object_offset_t ruletree_find_rule(
        const path_mapping_context_t *ctx,
	ruletree_object_offset_t rule_list_offs,
	const char *virtual_path,
	size_t virtual_path_len,
	int *min_path_lenp,
	uint32_t fn_class,
	ruletree_fsrule_t	**rule_p)
{
	uint32_t	rule_list_size;
	uint32_t	i;
	PROCESSCLOCK(clk1)

	START_PROCESSCLOCK(LB_LOGLEVEL_INFO, &clk1, "ruletree_find_rule");
	LB_LOG(LB_LOGLEVEL_NOISE, "ruletree_find_rule for (%s)", virtual_path);
	rule_list_size = ruletree_objectlist_get_list_size(rule_list_offs);

	if (min_path_lenp) *min_path_lenp = 0;
	if (rule_p) *rule_p = NULL;

	if (rule_list_size == 0) return(0);

	for (i = 0; i < rule_list_size; i++) {
		ruletree_fsrule_t	*rp;
		ruletree_object_offset_t rule_offs;

		rule_offs = ruletree_objectlist_get_item(rule_list_offs, i);
		if (!rule_offs) continue;

		rp = offset_to_ruletree_fsrule_ptr(rule_offs);
		if (rp) {
			int min_path_len;

			if (rp->rtree_fsr_condition_type != 0) {
				LB_LOG(LB_LOGLEVEL_DEBUG,
					"ruletree_find_rule: can't handle rules with conditions, fail. @%d", rule_offs);
				return(0);
			}

			if (rp->rtree_fsr_selector_type == 0) {
				LB_LOG(LB_LOGLEVEL_NOISE,
					"ruletree_find_rule skipping defunct rule @%d", rule_offs);
				continue;
			}

			min_path_len = ruletree_test_path_match(virtual_path, virtual_path_len, rp);

			if (min_path_len >= 0) {
				LB_LOG(LB_LOGLEVEL_NOISE,
					"ruletree_find_rule found rule @ %d",
					rule_offs);

				if (rp->rtree_fsr_func_class) {
					if ((rp->rtree_fsr_func_class & fn_class) == 0) {
						/* Function class does not match.. */
						continue;
					}
				}

				if (rp->rtree_fsr_binary_name) {
					const char	*bin_name_in_rule =
						offset_to_ruletree_string_ptr(rp->rtree_fsr_binary_name, NULL);
					if (strcmp(ctx->pmc_binary_name, bin_name_in_rule)) {
						/* binary name does not match, not this rule... */
						continue;
					}
				}

				if (min_path_lenp) *min_path_lenp = min_path_len;

				if (rp->rtree_fsr_action_type == LB_RULETREE_FSRULE_ACTION_SUBTREE) {
					/* if rule can be found from the subtree, return it,
                                         * otherwise continue looping here */
					if (rp->rtree_fsr_rule_list_link) {
						ruletree_object_offset_t subtree_offs;

						LB_LOG(LB_LOGLEVEL_NOISE,
							"ruletree_find_rule: continue @ %d",
							rp->rtree_fsr_rule_list_link);
						subtree_offs = ruletree_find_rule(ctx,
							rp->rtree_fsr_rule_list_link,
							virtual_path, virtual_path_len,
							min_path_lenp,
							fn_class, rule_p);
						if (subtree_offs) {
							STOP_AND_REPORT_PROCESSCLOCK(
								LB_LOGLEVEL_INFO, &clk1,
								"found/subtree");
							return(subtree_offs);
						}
					} else {
						LB_LOG(LB_LOGLEVEL_NOISE,
							"ruletree_find_rule: no link");
					}
					continue;
				}
				/* found it! */
				if (rule_p) *rule_p = rp;
				STOP_AND_REPORT_PROCESSCLOCK(
					LB_LOGLEVEL_INFO, &clk1,
					"found");
				return(rule_offs);
			}
		}
	}
	STOP_AND_REPORT_PROCESSCLOCK(LB_LOGLEVEL_INFO, &clk1, "not found");
	return (0); /* failed to find it */
}