From 33a7433ac97c3dc7e1c23ff02affdf65024c4279 Mon Sep 17 00:00:00 2001 From: Thierry FOURNIER Date: Thu, 19 Dec 2013 23:54:54 +0100 Subject: [PATCH] MEDIUM: pattern: Index IPv6 addresses in a tree. This commit adds second tree node in the pattern struct and use it to index IPv6 addresses. This commit report feature used in the list. If IPv4 not match the tree, try to convert the IPv4 address in IPv6 with prefixing the IPv4 address by "::ffff", after this operation, the match function try lookup in the IPv6 tree. If the IPv6 sample dont match the IPv6 tree, try to convert the IPv6 addresses prefixed by "2002:IPv4", "::ffff:IPv4" and "::0000:IPv4" in IPv4 address. after this operation, the match function try lookup in the IPv4 tree. --- include/types/pattern.h | 1 + src/pattern.c | 188 +++++++++++++++++++++++++++------------- 2 files changed, 128 insertions(+), 61 deletions(-) diff --git a/include/types/pattern.h b/include/types/pattern.h index 7a281716c..9622888a3 100644 --- a/include/types/pattern.h +++ b/include/types/pattern.h @@ -160,6 +160,7 @@ struct pattern_expr { struct pattern *(*match)(struct sample *, struct pattern_expr *, int); struct list patterns; /* list of acl_patterns */ struct eb_root pattern_tree; /* may be used for lookup in large datasets */ + struct eb_root pattern_tree_2; /* may be used for different types */ }; extern char *pat_match_names[PAT_MATCH_NUM]; diff --git a/src/pattern.c b/src/pattern.c index d6ae8d9f7..b417e9ffc 100644 --- a/src/pattern.c +++ b/src/pattern.c @@ -398,7 +398,6 @@ int pat_parse_ip(const char *text, struct pattern *pattern, char **err) return 1; } else if (str62net(text, &pattern->val.ipv6.addr, &pattern->val.ipv6.mask)) { - /* no tree support right now */ pattern->type = SMP_T_IPV6; return 1; } @@ -757,8 +756,6 @@ struct pattern *pat_match_len(struct sample *smp, struct pattern_expr *expr, int struct pattern *pat_match_ip(struct sample *smp, struct pattern_expr *expr, int fill) { unsigned int v4; /* in network byte order */ - struct in6_addr *v6; - int bits, pos; struct in6_addr tmp6; struct in_addr *s; struct ebmb_node *node; @@ -770,10 +767,11 @@ struct pattern *pat_match_ip(struct sample *smp, struct pattern_expr *expr, int if (!sample_convert(smp, SMP_T_ADDR)) return NULL; - /* Lookup an IPv4 address in the expression's pattern tree using the longest - * match method. - */ + /* The input sample is IPv4. Try to match in the trees. */ if (smp->type == SMP_T_IPV4) { + /* Lookup an IPv4 address in the expression's pattern tree using + * the longest match method. + */ s = &smp->data.ipv4; node = ebmb_lookup_longest(&expr->pattern_tree, &s->s_addr); if (node) { @@ -788,71 +786,114 @@ struct pattern *pat_match_ip(struct sample *smp, struct pattern_expr *expr, int } return &static_pattern; } + + /* The IPv4 sample dont match the IPv4 tree. Convert the IPv4 + * sample address to IPv6 with the mapping method using the ::ffff: + * prefix, and try to lookup in the IPv6 tree. + */ + memset(&tmp6, 0, 10); + *(uint16_t*)&tmp6.s6_addr[10] = htons(0xffff); + *(uint32_t*)&tmp6.s6_addr[12] = smp->data.ipv4.s_addr; + node = ebmb_lookup_longest(&expr->pattern_tree_2, &tmp6); + if (node) { + if (fill) { + elt = ebmb_entry(node, struct pattern_tree, node); + static_pattern.smp = elt->smp; + static_pattern.flags = PAT_F_TREE; + static_pattern.type = SMP_T_IPV6; + memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16); + static_pattern.val.ipv6.mask = elt->node.node.pfx; + } + return &static_pattern; + } } - /* Lookup in the list */ + /* The input sample is IPv6. Try to match in the trees. */ + if (smp->type == SMP_T_IPV6) { + /* Lookup an IPv6 address in the expression's pattern tree using + * the longest match method. + */ + node = ebmb_lookup_longest(&expr->pattern_tree_2, &smp->data.ipv6); + if (node) { + if (fill) { + elt = ebmb_entry(node, struct pattern_tree, node); + static_pattern.smp = elt->smp; + static_pattern.flags = PAT_F_TREE; + static_pattern.type = SMP_T_IPV6; + memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16); + static_pattern.val.ipv6.mask = elt->node.node.pfx; + } + return &static_pattern; + } + + /* Try to convert 6 to 4 when the start of the ipv6 address match 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))) || + *(uint16_t*)&smp->data.ipv6.s6_addr[0] == htons(0x2002)) { + if (*(uint32_t*)&smp->data.ipv6.s6_addr[0] == 0) + v4 = *(uint32_t*)&smp->data.ipv6.s6_addr[12]; + else + v4 = htonl((ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[2]) << 16) + + ntohs(*(uint16_t*)&smp->data.ipv6.s6_addr[4])); + + /* Lookup an IPv4 address in the expression's pattern tree using the longest + * match method. + */ + node = ebmb_lookup_longest(&expr->pattern_tree, &v4); + if (node) { + if (fill) { + elt = ebmb_entry(node, struct pattern_tree, node); + static_pattern.smp = elt->smp; + static_pattern.flags = PAT_F_TREE; + static_pattern.type = SMP_T_IPV4; + memcpy(&static_pattern.val.ipv4.addr.s_addr, elt->node.key, 4); + if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask)) + return NULL; + } + return &static_pattern; + } + } + } + + /* Lookup in the list. the list contain only IPv4 patterns */ list_for_each_entry(lst, &expr->patterns, list) { pattern = &lst->pat; - if (pattern->type == SMP_T_IPV4) { - if (smp->type == SMP_T_IPV4) { - v4 = smp->data.ipv4.s_addr; + /* The input sample is IPv4, use it as is. */ + 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 (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 - continue; + 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 continue; - - if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) - return pattern; - else - continue; } - 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 { - continue; - } - 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) - continue; - } + /* Check if the input sample match the current pattern. */ + if (((v4 ^ pattern->val.ipv4.addr.s_addr) & pattern->val.ipv4.mask.s_addr) == 0) return pattern; - } } return NULL; } @@ -901,6 +942,7 @@ void pattern_prune_expr(struct pattern_expr *expr) { free_pattern_list(&expr->patterns); free_pattern_tree(&expr->pattern_tree); + free_pattern_tree(&expr->pattern_tree_2); LIST_INIT(&expr->patterns); } @@ -908,6 +950,7 @@ void pattern_init_expr(struct pattern_expr *expr) { LIST_INIT(&expr->patterns); expr->pattern_tree = EB_ROOT_UNIQUE; + expr->pattern_tree_2 = EB_ROOT_UNIQUE; } /* @@ -1066,10 +1109,33 @@ int pat_idx_tree_ip(struct pattern_expr *expr, struct pattern *pat, char **err) /* that's ok */ return 1; } + else { + /* If the mask is not contiguous, just add the pattern to the list */ + return pat_idx_list_val(expr, pat, err); + } + } + else if (pat->type == SMP_T_IPV6) { + /* IPv6 also can be indexed */ + node = calloc(1, sizeof(*node) + 16); + if (!node) { + memprintf(err, "out of memory while loading pattern"); + return 0; + } + + /* copy the pointer to sample associated to this node */ + node->smp = pat->smp; + + /* FIXME: insert / into the tree here */ + memcpy(node->node.key, &pat->val.ipv6.addr, 16); /* network byte order */ + node->node.node.pfx = pat->val.ipv6.mask; + if (ebmb_insert_prefix(&expr->pattern_tree_2, &node->node, 16) != &node->node) + free(node); /* was a duplicate */ + + /* that's ok */ + return 1; } - /* If the value cannot be indexed, just add it to the list */ - return pat_idx_list_val(expr, pat, err); + return 0; } int pat_idx_tree_str(struct pattern_expr *expr, struct pattern *pat, char **err)