From e7ba23633bd8f457f1cfb2bd9d5bd80f8e9d831a Mon Sep 17 00:00:00 2001 From: Thierry FOURNIER Date: Tue, 21 Jan 2014 11:25:41 +0100 Subject: [PATCH] MINOR: pattern: move functions for grouping pat_match_* and pat_parse_* and add documentation. --- include/proto/pattern.h | 24 +- src/pattern.c | 609 ++++++++++++++++++++++------------------ 2 files changed, 342 insertions(+), 291 deletions(-) diff --git a/include/proto/pattern.h b/include/proto/pattern.h index 080b06223..e5c1b42df 100644 --- a/include/proto/pattern.h +++ b/include/proto/pattern.h @@ -70,18 +70,6 @@ enum pat_match_res pattern_exec_match(struct pattern_expr *expr, struct sample * /* ignore the current line */ int pat_parse_nothing(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); -/* NB: For two strings to be identical, it is required that their lengths match */ -enum pat_match_res pat_match_str(struct sample *smp, struct pattern *pattern); - -/* NB: For two binary buffers to be identical, it is required that their lengths match */ -enum pat_match_res pat_match_bin(struct sample *smp, struct pattern *pattern); - -/* Checks that the length of the pattern in is included between min and max */ -enum pat_match_res pat_match_len(struct sample *smp, struct pattern *pattern); - -/* Checks that the integer in is included between min and max */ -enum pat_match_res pat_match_int(struct sample *smp, struct pattern *pattern); - /* Parse an integer. It is put both in min and max. */ int pat_parse_int(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); @@ -115,6 +103,18 @@ int pat_parse_reg(const char **text, struct pattern *pattern, enum pat_usage usa */ int pat_parse_ip(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err); +/* NB: For two strings to be identical, it is required that their lengths match */ +enum pat_match_res pat_match_str(struct sample *smp, struct pattern *pattern); + +/* NB: For two binary buffers to be identical, it is required that their lengths match */ +enum pat_match_res pat_match_bin(struct sample *smp, struct pattern *pattern); + +/* Checks that the length of the pattern in is included between min and max */ +enum pat_match_res pat_match_len(struct sample *smp, struct pattern *pattern); + +/* Checks that the integer in is included between min and max */ +enum pat_match_res pat_match_int(struct sample *smp, struct pattern *pattern); + /* always return false */ enum pat_match_res pat_match_nothing(struct sample *smp, struct pattern *pattern); diff --git a/src/pattern.c b/src/pattern.c index b05320105..ce12a0e3e 100644 --- a/src/pattern.c +++ b/src/pattern.c @@ -90,46 +90,30 @@ int pat_match_types[PAT_MATCH_NUM] = { }; /* - * These functions are exported and may be used by any other component. + * + * The following functions are not exported and are used by internals process + * of pattern matching + * */ -/* ignore the current line */ -int pat_parse_nothing(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) +/* Lookup an IPv4 address in the expression's pattern tree using the longest + * match method. The node is returned if it exists, otherwise NULL. + */ +static void *pat_lookup_ip(struct sample *smp, struct pattern_expr *expr) { - return 1; -} + struct in_addr *s; -/* always return false */ -enum pat_match_res pat_match_nothing(struct sample *smp, struct pattern *pattern) -{ - return PAT_NOMATCH; -} - - -/* NB: For two strings to be identical, it is required that their lengths match */ -enum pat_match_res pat_match_str(struct sample *smp, struct pattern *pattern) -{ - int icase; - - if (pattern->len != smp->data.str.len) + if (smp->type != SMP_T_IPV4) return PAT_NOMATCH; - icase = pattern->flags & PAT_F_IGNORE_CASE; - if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) || - (!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0)) - return PAT_MATCH; - return PAT_NOMATCH; + s = &smp->data.ipv4; + return ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr); } -/* NB: For two binaries buf to be identical, it is required that their lengths match */ -enum pat_match_res pat_match_bin(struct sample *smp, struct pattern *pattern) +/* Free data allocated by pat_parse_reg */ +static void pat_free_reg(void *ptr) { - if (pattern->len != smp->data.str.len) - return PAT_NOMATCH; - - if (memcmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) - return PAT_MATCH; - return PAT_NOMATCH; + regex_free(ptr); } /* Lookup a string in the expression's pattern tree. The node is returned if it @@ -151,77 +135,6 @@ static void *pat_lookup_str(struct sample *smp, struct pattern_expr *expr) return node; } -/* Executes a regex. It temporarily changes the data to add a trailing zero, - * and restores the previous character when leaving. - */ -enum pat_match_res pat_match_reg(struct sample *smp, struct pattern *pattern) -{ - if (regex_exec(pattern->ptr.reg, smp->data.str.str, smp->data.str.len) == 0) - return PAT_MATCH; - return PAT_NOMATCH; -} - -/* Checks that the pattern matches the beginning of the tested string. */ -enum pat_match_res pat_match_beg(struct sample *smp, struct pattern *pattern) -{ - int icase; - - if (pattern->len > smp->data.str.len) - return PAT_NOMATCH; - - icase = pattern->flags & PAT_F_IGNORE_CASE; - if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) || - (!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0)) - return PAT_NOMATCH; - return PAT_MATCH; -} - -/* Checks that the pattern matches the end of the tested string. */ -enum pat_match_res pat_match_end(struct sample *smp, struct pattern *pattern) -{ - int icase; - - if (pattern->len > smp->data.str.len) - return PAT_NOMATCH; - icase = pattern->flags & PAT_F_IGNORE_CASE; - if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) || - (!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0)) - return PAT_NOMATCH; - return PAT_MATCH; -} - -/* Checks that the pattern is included inside the tested string. - * NB: Suboptimal, should be rewritten using a Boyer-Moore method. - */ -enum pat_match_res pat_match_sub(struct sample *smp, struct pattern *pattern) -{ - int icase; - char *end; - char *c; - - if (pattern->len > smp->data.str.len) - return PAT_NOMATCH; - - end = smp->data.str.str + smp->data.str.len - pattern->len; - icase = pattern->flags & PAT_F_IGNORE_CASE; - if (icase) { - for (c = smp->data.str.str; c <= end; c++) { - if (tolower(*c) != tolower(*pattern->ptr.str)) - continue; - if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0) - return PAT_MATCH; - } - } else { - for (c = smp->data.str.str; c <= end; c++) { - if (*c != *pattern->ptr.str) - continue; - if (strncmp(pattern->ptr.str, c, pattern->len) == 0) - return PAT_MATCH; - } - } - return PAT_NOMATCH; -} - /* Background: Fast way to find a zero byte in a word * http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord * hasZeroByte = (v - 0x01010101UL) & ~v & 0x80808080UL; @@ -244,178 +157,48 @@ static inline unsigned int make_4delim(unsigned char d1, unsigned char d2, unsig return d1 << 24 | d2 << 16 | d3 << 8 | d4; } -/* This one is used by other real functions. It checks that the pattern is - * included inside the tested string, but enclosed between the specified - * delimiters or at the beginning or end of the string. The delimiters are - * provided as an unsigned int made by make_4delim() and match up to 4 different - * delimiters. Delimiters are stripped at the beginning and end of the pattern. + +/* + * + * These functions are exported and may be used by any other component. + * + * The following functions are used for parsing pattern matching + * input value. The contain a list of word. The last entry + * must be one NULL character. the contain the string to be + * parsed. must be a preallocated pattern. The pat_parse_* + * functions fill this structure with the parsed value. can + * be PAT_U_COMPILE or PAT_U_LOOKUP. If the value PAT_U_COMPILE is + * used memory is allocated for filling the pattern. If the value + * PAT_U_LOOKUP is set, the parser use "trash" or return pointers + * to the input strings. In both cases, the caller must use the + * value PAT_U_LOOKUP with caution. is used to pass value + * between two calls to the parser. the interger must ben initilized + * to 0 (see note below). is filled with an error message built + * with memprintf() function. + * + * In succes case, the pat_parse_* function return the number of + * eated. If the function fail, it returns 0 and is + * filled. + * + * NOTE: iIt is used with integer range parser. The following + * configuration line is processed with this method: + * + * acl ... -m int eq 10 20 + * + * The first call to the parser eat 2 elements: "eq" and "10". The + * pattern is filled with "eq 10" content. The contain + * coded value value that represent "eq". + * + * The second call to the parser just eat 1 element: "20". The opaque + * contain the value of the operator. The parser returns pattern filled + * with "eq 20". + * */ -static int match_word(struct sample *smp, struct pattern *pattern, unsigned int delimiters) + +/* ignore the current line */ +int pat_parse_nothing(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { - int may_match, icase; - char *c, *end; - char *ps; - int pl; - - pl = pattern->len; - ps = pattern->ptr.str; - - while (pl > 0 && is_delimiter(*ps, delimiters)) { - pl--; - ps++; - } - - while (pl > 0 && is_delimiter(ps[pl - 1], delimiters)) - pl--; - - if (pl > smp->data.str.len) - return PAT_NOMATCH; - - may_match = 1; - icase = pattern->flags & PAT_F_IGNORE_CASE; - end = smp->data.str.str + smp->data.str.len - pl; - for (c = smp->data.str.str; c <= end; c++) { - if (is_delimiter(*c, delimiters)) { - may_match = 1; - continue; - } - - if (!may_match) - continue; - - if (icase) { - if ((tolower(*c) == tolower(*ps)) && - (strncasecmp(ps, c, pl) == 0) && - (c == end || is_delimiter(c[pl], delimiters))) - return PAT_MATCH; - } else { - if ((*c == *ps) && - (strncmp(ps, c, pl) == 0) && - (c == end || is_delimiter(c[pl], delimiters))) - return PAT_MATCH; - } - may_match = 0; - } - return PAT_NOMATCH; -} - -/* Checks that the pattern is included inside the tested string, but enclosed - * between the delimiters '?' or '/' or at the beginning or end of the string. - * Delimiters at the beginning or end of the pattern are ignored. - */ -enum pat_match_res pat_match_dir(struct sample *smp, struct pattern *pattern) -{ - return match_word(smp, pattern, make_4delim('/', '?', '?', '?')); -} - -/* Checks that the pattern is included inside the tested string, but enclosed - * between the delmiters '/', '?', '.' or ":" or at the beginning or end of - * the string. Delimiters at the beginning or end of the pattern are ignored. - */ -enum pat_match_res pat_match_dom(struct sample *smp, struct pattern *pattern) -{ - return match_word(smp, pattern, make_4delim('/', '?', '.', ':')); -} - -/* Checks that the integer in is included between min and max */ -enum pat_match_res pat_match_int(struct sample *smp, struct pattern *pattern) -{ - if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.uint) && - (!pattern->val.range.max_set || smp->data.uint <= pattern->val.range.max)) - return PAT_MATCH; - return PAT_NOMATCH; -} - -/* Checks that the length of the pattern in is included between min and max */ -enum pat_match_res pat_match_len(struct sample *smp, struct pattern *pattern) -{ - if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.str.len) && - (!pattern->val.range.max_set || smp->data.str.len <= pattern->val.range.max)) - return PAT_MATCH; - return PAT_NOMATCH; -} - -enum pat_match_res pat_match_ip(struct sample *smp, struct pattern *pattern) -{ - unsigned int v4; /* in network byte order */ - struct in6_addr *v6; - int bits, pos; - struct in6_addr tmp6; - - if (pattern->type == SMP_T_IPV4) { - if (smp->type == SMP_T_IPV4) { - v4 = smp->data.ipv4.s_addr; - } - else if (smp->type == SMP_T_IPV6) { - /* v4 match on a V6 sample. We want to check at least for - * the following forms : - * - ::ffff:ip:v4 (ipv4 mapped) - * - ::0000:ip:v4 (old ipv4 mapped) - * - 2002:ip:v4:: (6to4) - */ - if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0 && - *(uint32_t*)&smp->data.ipv6.s6_addr[4] == 0 && - (*(uint32_t*)&smp->data.ipv6.s6_addr[8] == 0 || - *(uint32_t*)&smp->data.ipv6.s6_addr[8] == htonl(0xFFFF))) { - v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12]; - } - else if (*(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) { - v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) + - ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4])); - } - else - return PAT_NOMATCH; - } - else - return PAT_NOMATCH; - - if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) - return PAT_MATCH; - else - return PAT_NOMATCH; - } - else if (pattern->type == SMP_T_IPV6) { - if (smp->type == SMP_T_IPV4) { - /* Convert the IPv4 sample address to IPv4 with the - * mapping method using the ::ffff: prefix. - */ - memset(&tmp6, 0, 10); - *(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff); - *(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr; - v6 = &tmp6; - } - else if (smp->type == SMP_T_IPV6) { - v6 = &smp->data.ipv6; - } - else { - return PAT_NOMATCH; - } - - bits = pattern->val.ipv6.mask; - for (pos = 0; bits > 0; pos += 4, bits -= 32) { - v4 = *(uint32_t*)&v6->s6_addr[pos] ^ *(uint32_t*)&pattern->val.ipv6.addr.s6_addr[pos]; - if (bits < 32) - v4 &= htonl((~0U) << (32-bits)); - if (v4) - return PAT_NOMATCH; - } - return PAT_MATCH; - } - return PAT_NOMATCH; -} - -/* Lookup an IPv4 address in the expression's pattern tree using the longest - * match method. The node is returned if it exists, otherwise NULL. - */ -static void *pat_lookup_ip(struct sample *smp, struct pattern_expr *expr) -{ - struct in_addr *s; - - if (smp->type != SMP_T_IPV4) - return PAT_NOMATCH; - - s = &smp->data.ipv4; - return ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr); + return 1; } /* Parse a string. It is allocated and duplicated. */ @@ -446,7 +229,7 @@ int pat_parse_bin(const char **text, struct pattern *pattern, enum pat_usage usa if (usage == PAT_U_COMPILE) /* If the parse_binary fails, it returns 0. In succes case, it returns - * the length of the arsed binary content. The function pat_parse_* + * the length of the arsed binary content. The functions pat_parse_* * must return 0 if fail and the number of elements eated from **text * if not fail. In succes case, this function eat always 1 elements. * The double operator "!" converts the range "1-n" to "1". @@ -498,12 +281,6 @@ pat_parse_strcat(const char **text, struct pattern *pattern, enum pat_usage usag return i; } -/* Free data allocated by pat_parse_reg */ -static void pat_free_reg(void *ptr) -{ - regex_free(ptr); -} - /* Parse a regex. It is allocated. */ int pat_parse_reg(const char **text, struct pattern *pattern, enum pat_usage usage, int *opaque, char **err) { @@ -768,6 +545,280 @@ int pat_parse_ip(const char **text, struct pattern *pattern, enum pat_usage usag } } +/* + * + * These functions are exported and may be used by any other component. + * + * This fucntion just take a sample and check if this sample match + * with the pattern . This fucntion return just PAT_MATCH or + * PAT_NOMATCH. + * + */ + +/* always return false */ +enum pat_match_res pat_match_nothing(struct sample *smp, struct pattern *pattern) +{ + return PAT_NOMATCH; +} + + +/* NB: For two strings to be identical, it is required that their lengths match */ +enum pat_match_res pat_match_str(struct sample *smp, struct pattern *pattern) +{ + int icase; + + if (pattern->len != smp->data.str.len) + return PAT_NOMATCH; + + icase = pattern->flags & PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) || + (!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0)) + return PAT_MATCH; + return PAT_NOMATCH; +} + +/* NB: For two binaries buf to be identical, it is required that their lengths match */ +enum pat_match_res pat_match_bin(struct sample *smp, struct pattern *pattern) +{ + if (pattern->len != smp->data.str.len) + return PAT_NOMATCH; + + if (memcmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) + return PAT_MATCH; + return PAT_NOMATCH; +} + +/* Executes a regex. It temporarily changes the data to add a trailing zero, + * and restores the previous character when leaving. + */ +enum pat_match_res pat_match_reg(struct sample *smp, struct pattern *pattern) +{ + if (regex_exec(pattern->ptr.reg, smp->data.str.str, smp->data.str.len) == 0) + return PAT_MATCH; + return PAT_NOMATCH; +} + +/* Checks that the pattern matches the beginning of the tested string. */ +enum pat_match_res pat_match_beg(struct sample *smp, struct pattern *pattern) +{ + int icase; + + if (pattern->len > smp->data.str.len) + return PAT_NOMATCH; + + icase = pattern->flags & PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) || + (!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0)) + return PAT_NOMATCH; + return PAT_MATCH; +} + +/* Checks that the pattern matches the end of the tested string. */ +enum pat_match_res pat_match_end(struct sample *smp, struct pattern *pattern) +{ + int icase; + + if (pattern->len > smp->data.str.len) + return PAT_NOMATCH; + icase = pattern->flags & PAT_F_IGNORE_CASE; + if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) || + (!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0)) + return PAT_NOMATCH; + return PAT_MATCH; +} + +/* Checks that the pattern is included inside the tested string. + * NB: Suboptimal, should be rewritten using a Boyer-Moore method. + */ +enum pat_match_res pat_match_sub(struct sample *smp, struct pattern *pattern) +{ + int icase; + char *end; + char *c; + + if (pattern->len > smp->data.str.len) + return PAT_NOMATCH; + + end = smp->data.str.str + smp->data.str.len - pattern->len; + icase = pattern->flags & PAT_F_IGNORE_CASE; + if (icase) { + for (c = smp->data.str.str; c <= end; c++) { + if (tolower(*c) != tolower(*pattern->ptr.str)) + continue; + if (strncasecmp(pattern->ptr.str, c, pattern->len) == 0) + return PAT_MATCH; + } + } else { + for (c = smp->data.str.str; c <= end; c++) { + if (*c != *pattern->ptr.str) + continue; + if (strncmp(pattern->ptr.str, c, pattern->len) == 0) + return PAT_MATCH; + } + } + return PAT_NOMATCH; +} + +/* This one is used by other real functions. It checks that the pattern is + * included inside the tested string, but enclosed between the specified + * delimiters or at the beginning or end of the string. The delimiters are + * provided as an unsigned int made by make_4delim() and match up to 4 different + * delimiters. Delimiters are stripped at the beginning and end of the pattern. + */ +static int match_word(struct sample *smp, struct pattern *pattern, unsigned int delimiters) +{ + int may_match, icase; + char *c, *end; + char *ps; + int pl; + + pl = pattern->len; + ps = pattern->ptr.str; + + while (pl > 0 && is_delimiter(*ps, delimiters)) { + pl--; + ps++; + } + + while (pl > 0 && is_delimiter(ps[pl - 1], delimiters)) + pl--; + + if (pl > smp->data.str.len) + return PAT_NOMATCH; + + may_match = 1; + icase = pattern->flags & PAT_F_IGNORE_CASE; + end = smp->data.str.str + smp->data.str.len - pl; + for (c = smp->data.str.str; c <= end; c++) { + if (is_delimiter(*c, delimiters)) { + may_match = 1; + continue; + } + + if (!may_match) + continue; + + if (icase) { + if ((tolower(*c) == tolower(*ps)) && + (strncasecmp(ps, c, pl) == 0) && + (c == end || is_delimiter(c[pl], delimiters))) + return PAT_MATCH; + } else { + if ((*c == *ps) && + (strncmp(ps, c, pl) == 0) && + (c == end || is_delimiter(c[pl], delimiters))) + return PAT_MATCH; + } + may_match = 0; + } + return PAT_NOMATCH; +} + +/* Checks that the pattern is included inside the tested string, but enclosed + * between the delimiters '?' or '/' or at the beginning or end of the string. + * Delimiters at the beginning or end of the pattern are ignored. + */ +enum pat_match_res pat_match_dir(struct sample *smp, struct pattern *pattern) +{ + return match_word(smp, pattern, make_4delim('/', '?', '?', '?')); +} + +/* Checks that the pattern is included inside the tested string, but enclosed + * between the delmiters '/', '?', '.' or ":" or at the beginning or end of + * the string. Delimiters at the beginning or end of the pattern are ignored. + */ +enum pat_match_res pat_match_dom(struct sample *smp, struct pattern *pattern) +{ + return match_word(smp, pattern, make_4delim('/', '?', '.', ':')); +} + +/* Checks that the integer in is included between min and max */ +enum pat_match_res pat_match_int(struct sample *smp, struct pattern *pattern) +{ + if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.uint) && + (!pattern->val.range.max_set || smp->data.uint <= pattern->val.range.max)) + return PAT_MATCH; + return PAT_NOMATCH; +} + +/* Checks that the length of the pattern in is included between min and max */ +enum pat_match_res pat_match_len(struct sample *smp, struct pattern *pattern) +{ + if ((!pattern->val.range.min_set || pattern->val.range.min <= smp->data.str.len) && + (!pattern->val.range.max_set || smp->data.str.len <= pattern->val.range.max)) + return PAT_MATCH; + return PAT_NOMATCH; +} + +enum pat_match_res pat_match_ip(struct sample *smp, struct pattern *pattern) +{ + unsigned int v4; /* in network byte order */ + struct in6_addr *v6; + int bits, pos; + struct in6_addr tmp6; + + if (pattern->type == SMP_T_IPV4) { + if (smp->type == SMP_T_IPV4) { + v4 = smp->data.ipv4.s_addr; + } + else if (smp->type == SMP_T_IPV6) { + /* v4 match on a V6 sample. We want to check at least for + * the following forms : + * - ::ffff:ip:v4 (ipv4 mapped) + * - ::0000:ip:v4 (old ipv4 mapped) + * - 2002:ip:v4:: (6to4) + */ + if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0 && + *(uint32_t*)&smp->data.ipv6.s6_addr[4] == 0 && + (*(uint32_t*)&smp->data.ipv6.s6_addr[8] == 0 || + *(uint32_t*)&smp->data.ipv6.s6_addr[8] == htonl(0xFFFF))) { + v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12]; + } + else if (*(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) { + v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) + + ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4])); + } + else + return PAT_NOMATCH; + } + else + return PAT_NOMATCH; + + if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) + return PAT_MATCH; + else + return PAT_NOMATCH; + } + else if (pattern->type == SMP_T_IPV6) { + if (smp->type == SMP_T_IPV4) { + /* Convert the IPv4 sample address to IPv4 with the + * mapping method using the ::ffff: prefix. + */ + memset(&tmp6, 0, 10); + *(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff); + *(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr; + v6 = &tmp6; + } + else if (smp->type == SMP_T_IPV6) { + v6 = &smp->data.ipv6; + } + else { + return PAT_NOMATCH; + } + + bits = pattern->val.ipv6.mask; + for (pos = 0; bits > 0; pos += 4, bits -= 32) { + v4 = *(uint32_t*)&v6->s6_addr[pos] ^ *(uint32_t*)&pattern->val.ipv6.addr.s6_addr[pos]; + if (bits < 32) + v4 &= htonl((~0U) << (32-bits)); + if (v4) + return PAT_NOMATCH; + } + return PAT_MATCH; + } + return PAT_NOMATCH; +} + /* NB: does nothing if is NULL */ void pattern_free(struct pattern *pat) {