BGP AS-PATH支持正则表达式源码分析

FRR的bgp实现正则表达式使用的是linux的正则表达式库函数:

#include <sys/types.h>
#include <regex.h>
int regcomp(regex_t *preg, const char *regex, int cflags);
int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
void regfree(regex_t *preg);

详细使用方法可以使用man进行查看。

本篇文章不是用来分析正则表达式如何使用的,而是为了说明BGP在使用正则表达式的独特差异时如何实现的。在FRR的正则表达式中有一个特殊符号,那就是'_'(下杠)。该符号在FRR中有特殊含义:它表示匹配[,{}() ]以及^和$,一共10个符号(包括空格也可以匹配)。

代码分析

/* Character `_' has special mean.  It represents [,{}() ] and the
   beginning of the line(^) and the end of the line ($).

   (^|[,{}() ]|$) (记住有空格)*/

regex_t *bgp_regcomp(const char *regstr)
{
	/* Convert _ character to generic regular expression. */
	int i, j;
	int len;
	int magic = 0;
	char *magic_str;
	char magic_regexp[] = "(^|[,{}() ]|$)";
	int ret;
	regex_t *regex;

	len = strlen(regstr);
    //统计正则表达式中的下杠的个数
	for (i = 0; i < len; i++)
		if (regstr[i] == '_')
			magic++;
    //将下杠替换成magic_regexp,一共14个字符,实际只需要分配13个字符即可,这里重新分配内存
	magic_str = XMALLOC(MTYPE_TMP, len + (14 * magic) + 1);
    //将下杠替换成magic_regexp
	for (i = 0, j = 0; i < len; i++) {
		if (regstr[i] == '_') {
			memcpy(magic_str + j, magic_regexp,
			       strlen(magic_regexp));
			j += strlen(magic_regexp);
		} else
			magic_str[j++] = regstr[i];
	}
	magic_str[j] = '\0';
    
	regex = XMALLOC(MTYPE,_BGP_REGEXP, sizeof(regex_t));
   //将正则表达式进行预编译,REG_NOSUB表示不报告匹配的位置以及次数,能加速匹配,REG_EXTENDED表示使用扩展正则表达式集合
	ret = regcomp(regex, magic_str, REG_EXTENDED | REG_NOSUB);

	XFREE(MTYPE_TMP, magic_str);

	if (ret != 0) {
		XFREE(MTYPE_BGP_REGEXP, regex);
		return NULL;
	}

	return regex;
}
//匹配函数
int bgp_regexec(regex_t *regex, struct aspath *aspath)
{
	return regexec(regex, aspath->str, 0, NULL, 0);
}

void bgp_regex_free(regex_t *regex)
{
	regfree(regex);
	XFREE(MTYPE_BGP_REGEXP, regex);
}

具体使用代码分析

添加正则表达式,并编译

DEFUN(as_path, bgp_as_path_cmd,
      "bgp as-path access-list WORD <deny|permit> LINE...",
      BGP_STR
      "BGP autonomous system path filter\n"
      "Specify an access list name\n"
      "Regular expression access list name\n"
      "Specify packets to reject\n"
      "Specify packets to forward\n"
      "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
{
	int idx = 0;
	enum as_filter_type type;
	struct as_filter *asfilter;
	struct as_list *aslist;
	regex_t *regex;
	char *regstr;

	if (argv_find(argv, argc, "ip", &idx)) {
		vty_out(vty, "This config option is deprecated and is scheduled for removal.\n");
		vty_out(vty, "if you are using this please migrate to the below command\n");
		vty_out(vty, "'bgp as-path access-list WORD <deny|permit> LINE'\n");
		zlog_warn("Deprecated option: 'ip as-path access-list WORD <deny|permit> LINE' being used");
	}

	/* Retrieve access list name */
	argv_find(argv, argc, "WORD", &idx);
	char *alname = argv[idx]->arg;

	/* Check the filter type. */
	type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
						   : AS_FILTER_PERMIT;

	/* Check AS path regex. */
	argv_find(argv, argc, "LINE", &idx);
	regstr = argv_concat(argv, argc, idx);
    //编译正则表达式
	regex = bgp_regcomp(regstr);
	if (!regex) {
		vty_out(vty, "can't compile regexp %s\n", regstr);
		XFREE(MTYPE_TMP, regstr);
		return CMD_WARNING_CONFIG_FAILED;
	}
    //正则表达式合规检查
	if (!config_bgp_aspath_validate(regstr)) {
		vty_out(vty, "Invalid character in as-path access-list %s\n",
			regstr);
		return CMD_WARNING_CONFIG_FAILED;
	}
    //构建asfilter上下文
	asfilter = as_filter_make(regex, regstr, type);

	XFREE(MTYPE_TMP, regstr);

	/* Install new filter to the access_list. */
	aslist = as_list_get(alname);

	/* Duplicate insertion check. */;
    /* 加入到过滤链表中 */
	if (as_list_dup_check(aslist, asfilter))
		as_filter_free(asfilter);
	else
		as_list_filter_add(aslist, asfilter);

	return CMD_SUCCESS;
}

具体使用正则表达式

/* Apply AS path filter to AS. */
enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
{
	struct as_filter *asfilter;
	struct aspath *aspath;

	aspath = (struct aspath *)object;

	if (aslist == NULL)
		return AS_FILTER_DENY;

	for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
		if (as_filter_match(asfilter, aspath))
			return asfilter->type;
	}
	return AS_FILTER_DENY;
}
static int as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
{
	if (bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH)
		return 1;
	return 0;
}
posted @ 2020-01-20 18:37  ouyangxibao  阅读(551)  评论(0)    收藏  举报