summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_flowspec_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_flowspec_util.c')
-rw-r--r--bgpd/bgp_flowspec_util.c685
1 files changed, 685 insertions, 0 deletions
diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c
new file mode 100644
index 0000000..66426ab
--- /dev/null
+++ b/bgpd/bgp_flowspec_util.c
@@ -0,0 +1,685 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP FlowSpec Utilities
+ * Portions:
+ * Copyright (C) 2017 ChinaTelecom SDN Group
+ * Copyright (C) 2018 6WIND
+ */
+
+#include "zebra.h"
+
+#include "lib/printfrr.h"
+
+#include "prefix.h"
+#include "lib_errors.h"
+
+#include "bgp_route.h"
+#include "bgp_table.h"
+#include "bgp_flowspec_util.h"
+#include "bgp_flowspec_private.h"
+#include "bgp_pbr.h"
+#include "bgp_errors.h"
+
+static void hex2bin(uint8_t *hex, int *bin)
+{
+ int remainder = *hex;
+ int i = 0;
+
+ while (remainder >= 1 && i < 8) {
+ bin[7-i] = remainder % 2;
+ remainder = remainder / 2;
+ i++;
+ }
+ for (; i < 8; i++)
+ bin[7-i] = 0;
+}
+
+static int hexstr2num(uint8_t *hexstr, int len)
+{
+ int i = 0;
+ int num = 0;
+
+ for (i = 0; i < len; i++)
+ num = hexstr[i] + 16*16*num;
+ return num;
+}
+
+/* call bgp_flowspec_op_decode
+ * returns offset
+ */
+static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len,
+ struct bgp_pbr_match_val *mval,
+ uint8_t *match_num, int *error)
+{
+ int ret;
+
+ ret = bgp_flowspec_op_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content,
+ len,
+ mval, error);
+ if (*error < 0)
+ flog_err(EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_op_decode error %d", __func__, *error);
+ else
+ *match_num = *error;
+ return ret;
+}
+
+
+bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
+ struct prefix *input, int prefix_check)
+{
+ uint32_t offset = 0;
+ int type;
+ int ret = 0, error = 0;
+ uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr;
+ size_t len = pfs->u.prefix_flowspec.prefixlen;
+ afi_t afi = family2afi(pfs->u.prefix_flowspec.family);
+ struct prefix compare;
+
+ error = 0;
+ while (offset < len-1 && error >= 0) {
+ type = nlri_content[offset];
+ offset++;
+ switch (type) {
+ case FLOWSPEC_DEST_PREFIX:
+ case FLOWSPEC_SRC_PREFIX:
+ memset(&compare, 0, sizeof(compare));
+ ret = bgp_flowspec_ip_address(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content+offset,
+ len - offset,
+ &compare, &error,
+ afi, NULL);
+ if (ret <= 0)
+ break;
+ if (prefix_check &&
+ compare.prefixlen != input->prefixlen)
+ break;
+ if (compare.family != input->family)
+ break;
+ if ((input->family == AF_INET) &&
+ IPV4_ADDR_SAME(&input->u.prefix4,
+ &compare.u.prefix4))
+ return true;
+ if ((input->family == AF_INET6) &&
+ IPV6_ADDR_SAME(&input->u.prefix6.s6_addr,
+ &compare.u.prefix6.s6_addr))
+ return true;
+ break;
+ case FLOWSPEC_FLOW_LABEL:
+ if (afi == AFI_IP) {
+ error = -1;
+ continue;
+ }
+ ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_IP_PROTOCOL:
+ case FLOWSPEC_PORT:
+ case FLOWSPEC_DEST_PORT:
+ case FLOWSPEC_SRC_PORT:
+ case FLOWSPEC_ICMP_TYPE:
+ case FLOWSPEC_ICMP_CODE:
+ ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_FRAGMENT:
+ case FLOWSPEC_TCP_FLAGS:
+ ret = bgp_flowspec_bitmask_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_PKT_LEN:
+ case FLOWSPEC_DSCP:
+ ret = bgp_flowspec_op_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL,
+ &error);
+ break;
+ default:
+ error = -1;
+ break;
+ }
+ offset += ret;
+ }
+ return false;
+}
+
+/*
+ * handle the flowspec address src/dst or generic address NLRI
+ * return number of bytes analysed ( >= 0).
+ */
+int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error,
+ afi_t afi,
+ uint8_t *ipv6_offset)
+{
+ char *display = (char *)result; /* for return_string */
+ struct prefix *prefix = (struct prefix *)result;
+ uint32_t offset = 0;
+ struct prefix prefix_local;
+ int psize;
+ uint8_t prefix_offset = 0;
+
+ *error = 0;
+ memset(&prefix_local, 0, sizeof(prefix_local));
+ /* read the prefix length */
+ prefix_local.prefixlen = nlri_ptr[offset];
+ psize = PSIZE(prefix_local.prefixlen);
+ offset++;
+ prefix_local.family = afi2family(afi);
+ if (prefix_local.family == AF_INET6) {
+ prefix_offset = nlri_ptr[offset];
+ if (ipv6_offset)
+ *ipv6_offset = prefix_offset;
+ offset++;
+ }
+ /* Prefix length check. */
+ if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) {
+ *error = -1;
+ return offset;
+ }
+ /* When packet overflow occur return immediately. */
+ if (psize + offset > max_len) {
+ *error = -1;
+ return offset;
+ }
+ /* Defensive coding, double-check
+ * the psize fits in a struct prefix
+ */
+ if (psize > (ssize_t)sizeof(prefix_local.u)) {
+ *error = -1;
+ return offset;
+ }
+
+ memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize);
+ offset += psize;
+ switch (type) {
+ case BGP_FLOWSPEC_RETURN_STRING:
+ if (prefix_local.family == AF_INET6) {
+ int ret;
+
+ ret = snprintfrr(
+ display, BGP_FLOWSPEC_STRING_DISPLAY_MAX,
+ "%pFX/off %u", &prefix_local, prefix_offset);
+ if (ret < 0) {
+ *error = -1;
+ break;
+ }
+ } else
+ prefix2str(&prefix_local, display,
+ BGP_FLOWSPEC_STRING_DISPLAY_MAX);
+ break;
+ case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
+ if (prefix)
+ prefix_copy(prefix, &prefix_local);
+ break;
+ case BGP_FLOWSPEC_VALIDATE_ONLY:
+ case BGP_FLOWSPEC_RETURN_JSON:
+ break;
+ }
+ return offset;
+}
+
+/*
+ * handle the flowspec operator NLRI
+ * return number of bytes analysed
+ * if there is an error, the passed error param is used to give error:
+ * -1 if decoding error,
+ * if result is a string, its assumed length
+ * is BGP_FLOWSPEC_STRING_DISPLAY_MAX
+ */
+int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error)
+{
+ int op[8];
+ int len, value, value_size;
+ int loop = 0;
+ char *ptr = (char *)result; /* for return_string */
+ uint32_t offset = 0;
+ int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
+ int len_written;
+ struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
+
+ *error = 0;
+ do {
+ if (loop > BGP_PBR_MATCH_VAL_MAX)
+ *error = -2;
+ hex2bin(&nlri_ptr[offset], op);
+ offset++;
+ len = 2*op[2]+op[3];
+ value_size = 1 << len;
+ value = hexstr2num(&nlri_ptr[offset], value_size);
+ /* can not be < and > at the same time */
+ if (op[5] == 1 && op[6] == 1)
+ *error = -1;
+ /* if first element, AND bit can not be set */
+ if (op[1] == 1 && loop == 0)
+ *error = -1;
+ switch (type) {
+ case BGP_FLOWSPEC_RETURN_STRING:
+ if (loop) {
+ len_written = snprintf(ptr, len_string,
+ ", ");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[5] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "<");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[6] == 1) {
+ len_written = snprintf(ptr, len_string,
+ ">");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[7] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "=");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ len_written = snprintf(ptr, len_string,
+ " %d ", value);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
+ /* limitation: stop converting */
+ if (*error == -2)
+ break;
+ mval->value = value;
+ if (op[5] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_LESS_THAN;
+ if (op[6] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_GREATER_THAN;
+ if (op[7] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EQUAL_TO;
+ if (op[1] == 1)
+ mval->unary_operator = OPERATOR_UNARY_AND;
+ else
+ mval->unary_operator = OPERATOR_UNARY_OR;
+ mval++;
+ break;
+ case BGP_FLOWSPEC_VALIDATE_ONLY:
+ case BGP_FLOWSPEC_RETURN_JSON:
+ /* no action */
+ break;
+ }
+ offset += value_size;
+ loop++;
+ } while (op[0] == 0 && offset < max_len - 1);
+ if (offset > max_len)
+ *error = -1;
+ /* use error parameter to count the number of entries */
+ if (*error == 0)
+ *error = loop;
+ return offset;
+}
+
+
+/*
+ * handle the flowspec tcpflags or fragment field
+ * return number of bytes analysed
+ * if there is an error, the passed error param is used to give error:
+ * -1 if decoding error,
+ * if result is a string, its assumed length
+ * is BGP_FLOWSPEC_STRING_DISPLAY_MAX
+ */
+int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error)
+{
+ int op[8];
+ int len, value_size, loop = 0, value;
+ char *ptr = (char *)result; /* for return_string */
+ struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
+ uint32_t offset = 0;
+ int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
+ int len_written;
+
+ *error = 0;
+ do {
+ if (loop > BGP_PBR_MATCH_VAL_MAX) {
+ *error = -2;
+ return offset;
+ }
+ hex2bin(&nlri_ptr[offset], op);
+ /* if first element, AND bit can not be set */
+ if (op[1] == 1 && loop == 0)
+ *error = -1;
+ offset++;
+ len = 2 * op[2] + op[3];
+ value_size = 1 << len;
+ value = hexstr2num(&nlri_ptr[offset], value_size);
+ switch (type) {
+ case BGP_FLOWSPEC_RETURN_STRING:
+ if (op[1] == 1 && loop != 0) {
+ len_written = snprintf(ptr, len_string,
+ ",&");
+ len_string -= len_written;
+ ptr += len_written;
+ } else if (op[1] == 0 && loop != 0) {
+ len_written = snprintf(ptr, len_string,
+ ",|");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[7] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "= ");
+ len_string -= len_written;
+ ptr += len_written;
+ } else {
+ len_written = snprintf(ptr, len_string,
+ "∋ ");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[6] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "! ");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ len_written = snprintf(ptr, len_string,
+ "%d", value);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
+ /* limitation: stop converting */
+ if (*error == -2)
+ break;
+ mval->value = value;
+ if (op[6] == 1) {
+ /* different from */
+ mval->compare_operator |=
+ OPERATOR_COMPARE_LESS_THAN;
+ mval->compare_operator |=
+ OPERATOR_COMPARE_GREATER_THAN;
+ } else
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EQUAL_TO;
+ if (op[7] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EXACT_MATCH;
+ if (op[1] == 1)
+ mval->unary_operator =
+ OPERATOR_UNARY_AND;
+ else
+ mval->unary_operator =
+ OPERATOR_UNARY_OR;
+ mval++;
+ break;
+ case BGP_FLOWSPEC_VALIDATE_ONLY:
+ case BGP_FLOWSPEC_RETURN_JSON:
+ /* no action */
+ break;
+ }
+ offset += value_size;
+ loop++;
+ } while (op[0] == 0 && offset < max_len - 1);
+ if (offset > max_len)
+ *error = -1;
+ /* use error parameter to count the number of entries */
+ if (*error == 0)
+ *error = loop;
+ return offset;
+}
+
+int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
+ struct bgp_pbr_entry_main *bpem,
+ afi_t afi)
+{
+ int offset = 0, error = 0;
+ struct prefix *prefix;
+ struct bgp_pbr_match_val *mval;
+ uint8_t *match_num;
+ uint8_t bitmask = 0;
+ int ret = 0, type;
+ uint8_t *prefix_offset;
+
+ while (offset < len - 1 && error >= 0) {
+ type = nlri_content[offset];
+ offset++;
+ switch (type) {
+ case FLOWSPEC_DEST_PREFIX:
+ case FLOWSPEC_SRC_PREFIX:
+ bitmask = 0;
+ if (type == FLOWSPEC_DEST_PREFIX) {
+ bitmask |= PREFIX_DST_PRESENT;
+ prefix = &bpem->dst_prefix;
+ prefix_offset = &bpem->dst_prefix_offset;
+ } else {
+ bitmask |= PREFIX_SRC_PRESENT;
+ prefix = &bpem->src_prefix;
+ prefix_offset = &bpem->src_prefix_offset;
+ }
+ ret = bgp_flowspec_ip_address(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset,
+ prefix, &error,
+ afi, prefix_offset);
+ if (error < 0)
+ flog_err(EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_ip_address error %d",
+ __func__, error);
+ else {
+ /* if src or dst address is 0.0.0.0,
+ * ignore that rule
+ */
+ if (prefix->family == AF_INET
+ && prefix->u.prefix4.s_addr == INADDR_ANY)
+ bpem->match_bitmask_iprule |= bitmask;
+ else if (prefix->family == AF_INET6
+ && !memcmp(&prefix->u.prefix6,
+ &in6addr_any,
+ sizeof(struct in6_addr)))
+ bpem->match_bitmask_iprule |= bitmask;
+ else
+ bpem->match_bitmask |= bitmask;
+ }
+ offset += ret;
+ break;
+ case FLOWSPEC_FLOW_LABEL:
+ if (afi == AFI_IP) {
+ error = -1;
+ continue;
+ }
+ match_num = &(bpem->match_flowlabel_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->flow_label);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_IP_PROTOCOL:
+ match_num = &(bpem->match_protocol_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->protocol);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_PORT:
+ match_num = &(bpem->match_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_DEST_PORT:
+ match_num = &(bpem->match_dst_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->dst_port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_SRC_PORT:
+ match_num = &(bpem->match_src_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->src_port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_ICMP_TYPE:
+ match_num = &(bpem->match_icmp_type_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->icmp_type);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_ICMP_CODE:
+ match_num = &(bpem->match_icmp_code_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->icmp_code);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_PKT_LEN:
+ match_num =
+ &(bpem->match_packet_length_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->packet_length);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_DSCP:
+ match_num = &(bpem->match_dscp_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->dscp);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_TCP_FLAGS:
+ ret = bgp_flowspec_bitmask_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset,
+ &bpem->tcpflags, &error);
+ if (error < 0)
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_tcpflags_decode error %d",
+ __func__, error);
+ else
+ bpem->match_tcpflags_num = error;
+ /* contains the number of slots used */
+ offset += ret;
+ break;
+ case FLOWSPEC_FRAGMENT:
+ ret = bgp_flowspec_bitmask_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset, &bpem->fragment,
+ &error);
+ if (error < 0)
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_fragment_type_decode error %d",
+ __func__, error);
+ else
+ bpem->match_fragment_num = error;
+ offset += ret;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: unknown type %d",
+ __func__, type);
+ }
+ }
+ if (bpem->match_packet_length_num || bpem->match_fragment_num
+ || bpem->match_tcpflags_num || bpem->match_dscp_num
+ || bpem->match_icmp_code_num || bpem->match_icmp_type_num
+ || bpem->match_port_num || bpem->match_src_port_num
+ || bpem->match_dst_port_num || bpem->match_protocol_num
+ || bpem->match_bitmask || bpem->match_flowlabel_num)
+ bpem->type = BGP_PBR_IPSET;
+ else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) ||
+ (bpem->match_bitmask_iprule & PREFIX_DST_PRESENT))
+ /* the extracted policy rule may not need an
+ * iptables/ipset filtering. check this may not be
+ * a standard ip rule : permit any to any ( eg)
+ */
+ bpem->type = BGP_PBR_IPRULE;
+ else
+ bpem->type = BGP_PBR_UNDEFINED;
+ return error;
+}
+
+/* return 1 if FS entry invalid or no NH IP */
+bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
+ struct prefix *p, afi_t afi)
+{
+ struct bgp_pbr_entry_main api;
+ int i;
+ struct bgp_dest *dest = pi->net;
+ struct bgp_pbr_entry_action *api_action;
+
+ memset(&api, 0, sizeof(api));
+ if (bgp_pbr_build_and_validate_entry(bgp_dest_get_prefix(dest), pi,
+ &api)
+ < 0)
+ return true;
+ for (i = 0; i < api.action_num; i++) {
+ api_action = &api.actions[i];
+ if (api_action->action != ACTION_REDIRECT_IP)
+ continue;
+ p->family = afi2family(afi);
+ if (afi == AFI_IP) {
+ p->prefixlen = IPV4_MAX_BITLEN;
+ p->u.prefix4 = api_action->u.zr.redirect_ip_v4;
+ } else {
+ p->prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&p->u.prefix6, &api_action->u.zr.redirect_ip_v6,
+ sizeof(struct in6_addr));
+ }
+ return false;
+ }
+ return true;
+}