From: Philippe Guibert Date: Tue, 12 Jun 2018 16:31:52 +0000 (+0200) Subject: bgpd: support for flowspec tcp flags X-Git-Tag: frr-6.1-dev~217^2~18 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=2da7d62e94997683e141e4b1d092eaedff16d3fb;p=matthieu%2Ffrr.git bgpd: support for flowspec tcp flags Ability to handle flowspec tcp flags. Signed-off-by: Philippe Guibert --- diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index c798a11a19..a554860647 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -183,6 +183,15 @@ struct bgp_pbr_range_port { uint16_t max_port; }; +/* this structure can be used to filter with a mask + * for instance it supports not instructions like for + * tcpflags + */ +struct bgp_pbr_val_mask { + uint16_t val; + uint16_t mask; +}; + /* this structure is used to pass instructs * so that BGP can create pbr instructions to ZEBRA */ @@ -194,18 +203,44 @@ struct bgp_pbr_filter { struct bgp_pbr_range_port *pkt_len; struct bgp_pbr_range_port *src_port; struct bgp_pbr_range_port *dst_port; + struct bgp_pbr_val_mask *tcp_flags; }; +/* TCP : FIN and SYN -> val = ALL; mask = 3 + * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) + */ static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[], - int num) + int num, uint8_t unary_operator, + struct bgp_pbr_val_mask *valmask) { int i = 0; + if (valmask) + memset(valmask, 0, sizeof(struct bgp_pbr_val_mask)); for (i = 0; i < num; i++) { - if (list[i].compare_operator != - OPERATOR_COMPARE_EQUAL_TO) + if (i != 0 && list[i].unary_operator != + unary_operator) return false; + if (!(list[i].compare_operator & + OPERATOR_COMPARE_EQUAL_TO) && + !(list[i].compare_operator & + OPERATOR_COMPARE_EXACT_MATCH)) { + if ((list[i].compare_operator & + OPERATOR_COMPARE_LESS_THAN) && + (list[i].compare_operator & + OPERATOR_COMPARE_GREATER_THAN)) { + if (valmask) + valmask->mask |= + TCP_HEADER_ALL_FLAGS & + ~(list[i].value); + continue; + } + return false; + } + if (valmask) + valmask->mask |= list[i].value; } + valmask->mask = TCP_HEADER_ALL_FLAGS; return true; } @@ -271,7 +306,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) * - combination src/dst => drop * - combination srcport + @IP */ - if (api->match_dscp_num || api->match_tcpflags_num) { + if (api->match_dscp_num) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); zlog_debug("BGP: some SET actions not supported by Zebra. ignoring."); @@ -309,9 +344,18 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "too complex. ignoring."); return 0; } + if (!bgp_pbr_extract_enumerate(api->tcpflags, + api->match_tcpflags_num, + OPERATOR_UNARY_AND, NULL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match tcp flags:" + "too complex. ignoring."); + return 0; + } if (!bgp_pbr_extract(api->icmp_type, api->match_icmp_type_num, NULL)) { if (!bgp_pbr_extract_enumerate(api->icmp_type, - api->match_icmp_type_num)) { + api->match_icmp_type_num, + OPERATOR_UNARY_OR, NULL)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match icmp type operations:" "too complex. ignoring."); @@ -321,7 +365,8 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } if (!bgp_pbr_extract(api->icmp_code, api->match_icmp_code_num, NULL)) { if (!bgp_pbr_extract_enumerate(api->icmp_code, - api->match_icmp_code_num)) { + api->match_icmp_code_num, + OPERATOR_UNARY_OR, NULL)) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match icmp code operations:" "too complex. ignoring."); @@ -591,6 +636,8 @@ uint32_t bgp_pbr_match_hash_key(void *arg) key = jhash_1word(pbm->flags, key); key = jhash_1word(pbm->pkt_len_min, key); key = jhash_1word(pbm->pkt_len_max, key); + key = jhash_1word(pbm->tcp_flags, key); + key = jhash_1word(pbm->tcp_mask_flags, key); return jhash_1word(pbm->type, key); } @@ -619,6 +666,12 @@ int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->pkt_len_max != r2->pkt_len_max) return 0; + if (r1->tcp_flags != r2->tcp_flags) + return 0; + + if (r1->tcp_mask_flags != r2->tcp_mask_flags) + return 0; + return 1; } @@ -1089,6 +1142,10 @@ static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, temp.pkt_len_min = pkt_len->min_port; if (pkt_len && pkt_len->max_port) temp.pkt_len_max = pkt_len->max_port; + if (bpf->tcp_flags) { + temp.tcp_flags = bpf->tcp_flags->val; + temp.tcp_mask_flags = bpf->tcp_flags->mask; + } if (bpf->src == NULL || bpf->dst == NULL) { if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET)) @@ -1155,12 +1212,15 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, char protocol_str[16]; protocol_str[0] = '\0'; + if (bpf->tcp_flags && bpf->tcp_flags->mask) + bpf->protocol = IPPROTO_TCP; if (bpf->protocol) snprintf(protocol_str, sizeof(protocol_str), "proto %d", bpf->protocol); buffer[0] = '\0'; if (bpf->protocol == IPPROTO_ICMP && src_port && dst_port) - remaining_len += snprintf(buffer, 64, "type %d, code %d", + remaining_len += snprintf(buffer, sizeof(buffer), + "type %d, code %d", src_port->min_port, dst_port->min_port); else if (bpf->protocol == IPPROTO_UDP || bpf->protocol == IPPROTO_TCP) { @@ -1194,6 +1254,14 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, pkt_len->max_port : pkt_len->min_port); } + if (bpf->tcp_flags) { + remaining_len += snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + "tcpflags %x/%x", + bpf->tcp_flags->val, + bpf->tcp_flags->mask); + } zlog_info("BGP: adding FS PBR from %s to %s, %s %s", bpf->src == NULL ? "" : prefix2str(bpf->src, bufsrc, sizeof(bufsrc)), @@ -1260,7 +1328,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, temp.pkt_len_min = pkt_len->min_port; if (pkt_len && pkt_len->max_port) temp.pkt_len_max = pkt_len->max_port; - + if (bpf->tcp_flags) { + temp.tcp_flags = bpf->tcp_flags->val; + temp.tcp_mask_flags = bpf->tcp_flags->mask; + } temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -1550,7 +1621,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, if (api->match_icmp_type_num >= 1) { proto = IPPROTO_ICMP; if (bgp_pbr_extract_enumerate(api->icmp_type, - api->match_icmp_type_num)) + api->match_icmp_type_num, + OPERATOR_UNARY_OR, NULL)) enum_icmp = true; else { bgp_pbr_extract(api->icmp_type, @@ -1562,7 +1634,8 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, if (api->match_icmp_code_num >= 1) { proto = IPPROTO_ICMP; if (bgp_pbr_extract_enumerate(api->icmp_code, - api->match_icmp_code_num)) + api->match_icmp_code_num, + OPERATOR_UNARY_OR, NULL)) enum_icmp = true; else { bgp_pbr_extract(api->icmp_code, @@ -1571,15 +1644,21 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, dstp = &range_icmp_code; } } - if (api->match_packet_length_num >= 1) + + if (api->match_tcpflags_num) + bgp_pbr_extract_enumerate(api->tcpflags, + api->match_tcpflags_num, + OPERATOR_UNARY_AND, bpf.tcp_flags); + if (api->match_packet_length_num >= 1) { bgp_pbr_extract(api->packet_length, api->match_packet_length_num, &pkt_len); + bpf.pkt_len = &pkt_len; + } bpf.vrf_id = api->vrf_id; bpf.src = src; bpf.dst = dst; bpf.protocol = proto; - bpf.pkt_len = &pkt_len; bpf.src_port = srcp; bpf.dst_port = dstp; if (!add) { diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 9d578a183b..a39fba82c1 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -134,6 +134,7 @@ struct bgp_pbr_entry_main { struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_fragment_val fragment; @@ -187,6 +188,8 @@ struct bgp_pbr_match { uint16_t pkt_len_min; uint16_t pkt_len_max; + uint16_t tcp_flags; + uint16_t tcp_mask_flags; vrf_id_t vrf_id; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 8cf7b29396..d201b48bad 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2221,6 +2221,8 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, ZEBRA_IPSET_NAME_SIZE); stream_putw(s, pbm->pkt_len_min); stream_putw(s, pbm->pkt_len_max); + stream_putw(s, pbm->tcp_flags); + stream_putw(s, pbm->tcp_mask_flags); } /* BGP has established connection with Zebra. */ diff --git a/lib/pbr.h b/lib/pbr.h index 1704d8d37e..0f9dfdd09c 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -89,6 +89,20 @@ struct pbr_rule { uint32_t ifindex; }; +/* TCP flags value shared + * those are values of byte 13 of TCP header + * as mentioned in rfc793 + */ +#define TCP_HEADER_FIN (0x01) +#define TCP_HEADER_SYN (0x02) +#define TCP_HEADER_RST (0x04) +#define TCP_HEADER_PSH (0x08) +#define TCP_HEADER_ACK (0x10) +#define TCP_HEADER_URG (0x20) +#define TCP_HEADER_ALL_FLAGS (TCP_HEADER_FIN | TCP_HEADER_SYN \ + | TCP_HEADER_RST | TCP_HEADER_PSH \ + | TCP_HEADER_ACK | TCP_HEADER_URG) + extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule);