mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 06:38:04 +00:00 
			
		
		
		
	remove warnings related to line too long in bgp code. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
		
			
				
	
	
		
			2979 lines
		
	
	
		
			81 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2979 lines
		
	
	
		
			81 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * BGP pbr
 | 
						|
 * Copyright (C) 6WIND
 | 
						|
 *
 | 
						|
 * FRR is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License as published by the
 | 
						|
 * Free Software Foundation; either version 2, or (at your option) any
 | 
						|
 * later version.
 | 
						|
 *
 | 
						|
 * FRR is distributed in the hope that it will be useful, but
 | 
						|
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License along
 | 
						|
 * with this program; see the file COPYING; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
#include "zebra.h"
 | 
						|
#include "prefix.h"
 | 
						|
#include "zclient.h"
 | 
						|
#include "jhash.h"
 | 
						|
#include "pbr.h"
 | 
						|
 | 
						|
#include "bgpd/bgpd.h"
 | 
						|
#include "bgpd/bgp_pbr.h"
 | 
						|
#include "bgpd/bgp_debug.h"
 | 
						|
#include "bgpd/bgp_flowspec_util.h"
 | 
						|
#include "bgpd/bgp_ecommunity.h"
 | 
						|
#include "bgpd/bgp_route.h"
 | 
						|
#include "bgpd/bgp_attr.h"
 | 
						|
#include "bgpd/bgp_zebra.h"
 | 
						|
#include "bgpd/bgp_mplsvpn.h"
 | 
						|
#include "bgpd/bgp_flowspec_private.h"
 | 
						|
#include "bgpd/bgp_errors.h"
 | 
						|
 | 
						|
DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry")
 | 
						|
DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match")
 | 
						|
DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action")
 | 
						|
DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule")
 | 
						|
DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context")
 | 
						|
DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value")
 | 
						|
 | 
						|
/* chain strings too long to fit in one line */
 | 
						|
#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit"
 | 
						|
#define IPV6_FRAGMENT_INVALID "fragment not valid for IPv6 for this implementation"
 | 
						|
 | 
						|
RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface,
 | 
						|
	    id_entry, bgp_pbr_interface_compare);
 | 
						|
struct bgp_pbr_interface_head ifaces_by_name_ipv4 =
 | 
						|
	RB_INITIALIZER(&ifaces_by_name_ipv4);
 | 
						|
 | 
						|
static int bgp_pbr_match_counter_unique;
 | 
						|
static int bgp_pbr_match_entry_counter_unique;
 | 
						|
static int bgp_pbr_action_counter_unique;
 | 
						|
static int bgp_pbr_match_iptable_counter_unique;
 | 
						|
 | 
						|
struct bgp_pbr_match_iptable_unique {
 | 
						|
	uint32_t unique;
 | 
						|
	struct bgp_pbr_match *bpm_found;
 | 
						|
};
 | 
						|
 | 
						|
struct bgp_pbr_match_entry_unique {
 | 
						|
	uint32_t unique;
 | 
						|
	struct bgp_pbr_match_entry *bpme_found;
 | 
						|
};
 | 
						|
 | 
						|
struct bgp_pbr_action_unique {
 | 
						|
	uint32_t unique;
 | 
						|
	struct bgp_pbr_action *bpa_found;
 | 
						|
};
 | 
						|
 | 
						|
struct bgp_pbr_rule_unique {
 | 
						|
	uint32_t unique;
 | 
						|
	struct bgp_pbr_rule *bpr_found;
 | 
						|
};
 | 
						|
 | 
						|
static int bgp_pbr_rule_walkcb(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_rule *bpr = (struct bgp_pbr_rule *)bucket->data;
 | 
						|
	struct bgp_pbr_rule_unique *bpru = (struct bgp_pbr_rule_unique *)
 | 
						|
		arg;
 | 
						|
	uint32_t unique = bpru->unique;
 | 
						|
 | 
						|
	if (bpr->unique == unique) {
 | 
						|
		bpru->bpr_found = bpr;
 | 
						|
		return HASHWALK_ABORT;
 | 
						|
	}
 | 
						|
	return HASHWALK_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static int bgp_pbr_action_walkcb(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_action *bpa = (struct bgp_pbr_action *)bucket->data;
 | 
						|
	struct bgp_pbr_action_unique *bpau = (struct bgp_pbr_action_unique *)
 | 
						|
		arg;
 | 
						|
	uint32_t unique = bpau->unique;
 | 
						|
 | 
						|
	if (bpa->unique == unique) {
 | 
						|
		bpau->bpa_found = bpa;
 | 
						|
		return HASHWALK_ABORT;
 | 
						|
	}
 | 
						|
	return HASHWALK_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static int bgp_pbr_match_entry_walkcb(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match_entry *bpme =
 | 
						|
		(struct bgp_pbr_match_entry *)bucket->data;
 | 
						|
	struct bgp_pbr_match_entry_unique *bpmeu =
 | 
						|
		(struct bgp_pbr_match_entry_unique *)arg;
 | 
						|
	uint32_t unique = bpmeu->unique;
 | 
						|
 | 
						|
	if (bpme->unique == unique) {
 | 
						|
		bpmeu->bpme_found = bpme;
 | 
						|
		return HASHWALK_ABORT;
 | 
						|
	}
 | 
						|
	return HASHWALK_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_match_ipsetname {
 | 
						|
	char *ipsetname;
 | 
						|
	struct bgp_pbr_match *bpm_found;
 | 
						|
};
 | 
						|
 | 
						|
static int bgp_pbr_match_pername_walkcb(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
 | 
						|
	struct bgp_pbr_match_ipsetname *bpmi =
 | 
						|
		(struct bgp_pbr_match_ipsetname *)arg;
 | 
						|
	char *ipset_name = bpmi->ipsetname;
 | 
						|
 | 
						|
	if (!strncmp(ipset_name, bpm->ipset_name,
 | 
						|
		     ZEBRA_IPSET_NAME_SIZE)) {
 | 
						|
		bpmi->bpm_found = bpm;
 | 
						|
		return HASHWALK_ABORT;
 | 
						|
	}
 | 
						|
	return HASHWALK_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static int bgp_pbr_match_iptable_walkcb(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
 | 
						|
	struct bgp_pbr_match_iptable_unique *bpmiu =
 | 
						|
		(struct bgp_pbr_match_iptable_unique *)arg;
 | 
						|
	uint32_t unique = bpmiu->unique;
 | 
						|
 | 
						|
	if (bpm->unique2 == unique) {
 | 
						|
		bpmiu->bpm_found = bpm;
 | 
						|
		return HASHWALK_ABORT;
 | 
						|
	}
 | 
						|
	return HASHWALK_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_match_unique {
 | 
						|
	uint32_t unique;
 | 
						|
	struct bgp_pbr_match *bpm_found;
 | 
						|
};
 | 
						|
 | 
						|
static int bgp_pbr_match_walkcb(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
 | 
						|
	struct bgp_pbr_match_unique *bpmu = (struct bgp_pbr_match_unique *)
 | 
						|
		arg;
 | 
						|
	uint32_t unique = bpmu->unique;
 | 
						|
 | 
						|
	if (bpm->unique == unique) {
 | 
						|
		bpmu->bpm_found = bpm;
 | 
						|
		return HASHWALK_ABORT;
 | 
						|
	}
 | 
						|
	return HASHWALK_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static int snprintf_bgp_pbr_match_val(char *str, int len,
 | 
						|
				      struct bgp_pbr_match_val *mval,
 | 
						|
				      const char *prepend)
 | 
						|
{
 | 
						|
	char *ptr = str;
 | 
						|
	int delta;
 | 
						|
 | 
						|
	if (prepend) {
 | 
						|
		delta = snprintf(ptr, len, "%s", prepend);
 | 
						|
		ptr += delta;
 | 
						|
		len -= delta;
 | 
						|
	} else {
 | 
						|
		if (mval->unary_operator & OPERATOR_UNARY_OR) {
 | 
						|
			delta = snprintf(ptr, len, ", or ");
 | 
						|
			ptr += delta;
 | 
						|
			len -= delta;
 | 
						|
		}
 | 
						|
		if (mval->unary_operator & OPERATOR_UNARY_AND) {
 | 
						|
			delta = snprintf(ptr, len, ", and ");
 | 
						|
			ptr += delta;
 | 
						|
			len -= delta;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) {
 | 
						|
		delta = snprintf(ptr, len, "<");
 | 
						|
		ptr += delta;
 | 
						|
		len -= delta;
 | 
						|
	}
 | 
						|
	if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) {
 | 
						|
		delta = snprintf(ptr, len, ">");
 | 
						|
		ptr += delta;
 | 
						|
		len -= delta;
 | 
						|
	}
 | 
						|
	if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) {
 | 
						|
		delta = snprintf(ptr, len, "=");
 | 
						|
		ptr += delta;
 | 
						|
		len -= delta;
 | 
						|
	}
 | 
						|
	if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) {
 | 
						|
		delta = snprintf(ptr, len, "match");
 | 
						|
		ptr += delta;
 | 
						|
		len -= delta;
 | 
						|
	}
 | 
						|
	ptr += snprintf(ptr, len, " %u", mval->value);
 | 
						|
	return (int)(ptr - str);
 | 
						|
}
 | 
						|
 | 
						|
#define INCREMENT_DISPLAY(_ptr, _cnt, _len) do {	\
 | 
						|
		int sn_delta;				\
 | 
						|
							\
 | 
						|
		if (_cnt) {				\
 | 
						|
			sn_delta = snprintf((_ptr), (_len), "; ");\
 | 
						|
			(_len) -= sn_delta;		\
 | 
						|
			(_ptr) += sn_delta;		\
 | 
						|
		}				\
 | 
						|
		(_cnt)++;	\
 | 
						|
	} while (0)
 | 
						|
 | 
						|
/* this structure can be used for port range,
 | 
						|
 * but also for other values range like packet length range
 | 
						|
 */
 | 
						|
struct bgp_pbr_range_port {
 | 
						|
	uint16_t min_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
 | 
						|
 */
 | 
						|
struct bgp_pbr_filter {
 | 
						|
	uint8_t type;
 | 
						|
	vrf_id_t vrf_id;
 | 
						|
	uint8_t family;
 | 
						|
	struct prefix *src;
 | 
						|
	struct prefix *dst;
 | 
						|
	uint8_t bitmask_iprule;
 | 
						|
	uint8_t protocol;
 | 
						|
	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;
 | 
						|
	struct bgp_pbr_val_mask *dscp;
 | 
						|
	struct bgp_pbr_val_mask *flow_label;
 | 
						|
	struct bgp_pbr_val_mask *pkt_len_val;
 | 
						|
	struct bgp_pbr_val_mask *fragment;
 | 
						|
};
 | 
						|
 | 
						|
/* this structure is used to contain OR instructions
 | 
						|
 * so that BGP can create multiple pbr instructions
 | 
						|
 * to ZEBRA
 | 
						|
 */
 | 
						|
struct bgp_pbr_or_filter {
 | 
						|
	struct list *tcpflags;
 | 
						|
	struct list *dscp;
 | 
						|
	struct list *flowlabel;
 | 
						|
	struct list *pkt_len;
 | 
						|
	struct list *fragment;
 | 
						|
	struct list *icmp_type;
 | 
						|
	struct list *icmp_code;
 | 
						|
};
 | 
						|
 | 
						|
static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
 | 
						|
						  struct bgp_path_info *path,
 | 
						|
						  struct bgp_pbr_filter *bpf,
 | 
						|
						  struct nexthop *nh,
 | 
						|
						  float *rate);
 | 
						|
 | 
						|
static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add);
 | 
						|
 | 
						|
static bool bgp_pbr_extract_enumerate_unary_opposite(
 | 
						|
				 uint8_t unary_operator,
 | 
						|
				 struct bgp_pbr_val_mask *and_valmask,
 | 
						|
				 struct list *or_valmask, uint32_t value,
 | 
						|
				 uint8_t type_entry)
 | 
						|
{
 | 
						|
	if (unary_operator == OPERATOR_UNARY_AND && and_valmask) {
 | 
						|
		if (type_entry == FLOWSPEC_TCP_FLAGS) {
 | 
						|
			and_valmask->mask |=
 | 
						|
				TCP_HEADER_ALL_FLAGS &
 | 
						|
				~(value);
 | 
						|
		} else if (type_entry == FLOWSPEC_DSCP ||
 | 
						|
			   type_entry == FLOWSPEC_FLOW_LABEL ||
 | 
						|
			   type_entry == FLOWSPEC_PKT_LEN ||
 | 
						|
			   type_entry == FLOWSPEC_FRAGMENT) {
 | 
						|
			and_valmask->val = value;
 | 
						|
			and_valmask->mask = 1; /* inverse */
 | 
						|
		}
 | 
						|
	} else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) {
 | 
						|
		and_valmask = XCALLOC(MTYPE_PBR_VALMASK,
 | 
						|
				      sizeof(struct bgp_pbr_val_mask));
 | 
						|
		if (type_entry == FLOWSPEC_TCP_FLAGS) {
 | 
						|
			and_valmask->val = TCP_HEADER_ALL_FLAGS;
 | 
						|
			and_valmask->mask |=
 | 
						|
				TCP_HEADER_ALL_FLAGS &
 | 
						|
				~(value);
 | 
						|
		} else if (type_entry == FLOWSPEC_DSCP ||
 | 
						|
			   type_entry == FLOWSPEC_FLOW_LABEL ||
 | 
						|
			   type_entry == FLOWSPEC_FRAGMENT ||
 | 
						|
			   type_entry == FLOWSPEC_PKT_LEN) {
 | 
						|
			and_valmask->val = value;
 | 
						|
			and_valmask->mask = 1; /* inverse */
 | 
						|
		}
 | 
						|
		listnode_add(or_valmask, and_valmask);
 | 
						|
	} else if (type_entry == FLOWSPEC_ICMP_CODE ||
 | 
						|
		   type_entry == FLOWSPEC_ICMP_TYPE)
 | 
						|
		return false;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/* TCP : FIN and SYN -> val = ALL; mask = 3
 | 
						|
 * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST)
 | 
						|
 * other variables type: dscp, pkt len, fragment, flow label
 | 
						|
 * - value is copied in bgp_pbr_val_mask->val value
 | 
						|
 * - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1
 | 
						|
 */
 | 
						|
static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[],
 | 
						|
					    int num, uint8_t unary_operator,
 | 
						|
					    void *valmask, uint8_t type_entry)
 | 
						|
{
 | 
						|
	int i = 0;
 | 
						|
	struct bgp_pbr_val_mask *and_valmask = NULL;
 | 
						|
	struct list *or_valmask = NULL;
 | 
						|
	bool ret;
 | 
						|
 | 
						|
	if (valmask) {
 | 
						|
		if (unary_operator == OPERATOR_UNARY_AND) {
 | 
						|
			and_valmask = (struct bgp_pbr_val_mask *)valmask;
 | 
						|
			memset(and_valmask, 0, sizeof(struct bgp_pbr_val_mask));
 | 
						|
		} else if (unary_operator == OPERATOR_UNARY_OR) {
 | 
						|
			or_valmask = (struct list *)valmask;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	for (i = 0; i < num; i++) {
 | 
						|
		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)) {
 | 
						|
				ret = bgp_pbr_extract_enumerate_unary_opposite(
 | 
						|
						 unary_operator, and_valmask,
 | 
						|
						 or_valmask, list[i].value,
 | 
						|
						 type_entry);
 | 
						|
				if (!ret)
 | 
						|
					return ret;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
		if (unary_operator == OPERATOR_UNARY_AND && and_valmask) {
 | 
						|
			if (type_entry == FLOWSPEC_TCP_FLAGS)
 | 
						|
				and_valmask->mask |=
 | 
						|
					TCP_HEADER_ALL_FLAGS & list[i].value;
 | 
						|
		} else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) {
 | 
						|
			and_valmask = XCALLOC(MTYPE_PBR_VALMASK,
 | 
						|
					      sizeof(struct bgp_pbr_val_mask));
 | 
						|
			if (type_entry == FLOWSPEC_TCP_FLAGS) {
 | 
						|
				and_valmask->val = TCP_HEADER_ALL_FLAGS;
 | 
						|
				and_valmask->mask |=
 | 
						|
					TCP_HEADER_ALL_FLAGS & list[i].value;
 | 
						|
			} else if (type_entry == FLOWSPEC_DSCP ||
 | 
						|
				   type_entry == FLOWSPEC_FLOW_LABEL ||
 | 
						|
				   type_entry == FLOWSPEC_ICMP_TYPE ||
 | 
						|
				   type_entry == FLOWSPEC_ICMP_CODE ||
 | 
						|
				   type_entry == FLOWSPEC_FRAGMENT ||
 | 
						|
				   type_entry == FLOWSPEC_PKT_LEN)
 | 
						|
				and_valmask->val = list[i].value;
 | 
						|
			listnode_add(or_valmask, and_valmask);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (unary_operator == OPERATOR_UNARY_AND && and_valmask
 | 
						|
	    && type_entry == FLOWSPEC_TCP_FLAGS)
 | 
						|
		and_valmask->val = TCP_HEADER_ALL_FLAGS;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/* if unary operator can either be UNARY_OR/AND/OR-AND.
 | 
						|
 * in the latter case, combinationf of both is not handled
 | 
						|
 */
 | 
						|
static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[],
 | 
						|
				      int num, uint8_t unary_operator,
 | 
						|
				      void *valmask, uint8_t type_entry)
 | 
						|
{
 | 
						|
	bool ret;
 | 
						|
	uint8_t unary_operator_val;
 | 
						|
	bool double_check = false;
 | 
						|
 | 
						|
	if ((unary_operator & OPERATOR_UNARY_OR) &&
 | 
						|
	    (unary_operator & OPERATOR_UNARY_AND)) {
 | 
						|
		unary_operator_val = OPERATOR_UNARY_AND;
 | 
						|
		double_check = true;
 | 
						|
	} else
 | 
						|
		unary_operator_val = unary_operator;
 | 
						|
	ret = bgp_pbr_extract_enumerate_unary(list, num, unary_operator_val,
 | 
						|
					      valmask, type_entry);
 | 
						|
	if (!ret && double_check)
 | 
						|
		ret = bgp_pbr_extract_enumerate_unary(list, num,
 | 
						|
						      OPERATOR_UNARY_OR,
 | 
						|
						      valmask,
 | 
						|
						      type_entry);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* returns the unary operator that is in the list
 | 
						|
 * return 0 if both operators are used
 | 
						|
 */
 | 
						|
static uint8_t bgp_pbr_match_val_get_operator(struct bgp_pbr_match_val list[],
 | 
						|
					      int num)
 | 
						|
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	uint8_t unary_operator = OPERATOR_UNARY_AND;
 | 
						|
 | 
						|
	for (i = 0; i < num; i++) {
 | 
						|
		if (i == 0)
 | 
						|
			continue;
 | 
						|
		if (list[i].unary_operator & OPERATOR_UNARY_OR)
 | 
						|
			unary_operator = OPERATOR_UNARY_OR;
 | 
						|
		if ((list[i].unary_operator & OPERATOR_UNARY_AND
 | 
						|
		     && unary_operator == OPERATOR_UNARY_OR) ||
 | 
						|
		    (list[i].unary_operator & OPERATOR_UNARY_OR
 | 
						|
		     && unary_operator == OPERATOR_UNARY_AND))
 | 
						|
			return 0;
 | 
						|
	}
 | 
						|
	return unary_operator;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* return true if extraction ok
 | 
						|
 */
 | 
						|
static bool bgp_pbr_extract(struct bgp_pbr_match_val list[],
 | 
						|
			    int num,
 | 
						|
			    struct bgp_pbr_range_port *range)
 | 
						|
{
 | 
						|
	int i = 0;
 | 
						|
	bool exact_match = false;
 | 
						|
 | 
						|
	if (range)
 | 
						|
		memset(range, 0, sizeof(struct bgp_pbr_range_port));
 | 
						|
 | 
						|
	if (num > 2)
 | 
						|
		return false;
 | 
						|
	for (i = 0; i < num; i++) {
 | 
						|
		if (i != 0 && (list[i].compare_operator ==
 | 
						|
			       OPERATOR_COMPARE_EQUAL_TO))
 | 
						|
			return false;
 | 
						|
		if (i == 0 && (list[i].compare_operator ==
 | 
						|
			       OPERATOR_COMPARE_EQUAL_TO)) {
 | 
						|
			if (range)
 | 
						|
				range->min_port = list[i].value;
 | 
						|
			exact_match = true;
 | 
						|
		}
 | 
						|
		if (exact_match && i > 0)
 | 
						|
			return false;
 | 
						|
		if (list[i].compare_operator ==
 | 
						|
		    (OPERATOR_COMPARE_GREATER_THAN +
 | 
						|
		     OPERATOR_COMPARE_EQUAL_TO)) {
 | 
						|
			if (range)
 | 
						|
				range->min_port = list[i].value;
 | 
						|
		} else if (list[i].compare_operator ==
 | 
						|
			   (OPERATOR_COMPARE_LESS_THAN +
 | 
						|
			    OPERATOR_COMPARE_EQUAL_TO)) {
 | 
						|
			if (range)
 | 
						|
				range->max_port = list[i].value;
 | 
						|
		} else if (list[i].compare_operator ==
 | 
						|
			   OPERATOR_COMPARE_LESS_THAN) {
 | 
						|
			if (range)
 | 
						|
				range->max_port = list[i].value - 1;
 | 
						|
		} else if (list[i].compare_operator ==
 | 
						|
			   OPERATOR_COMPARE_GREATER_THAN) {
 | 
						|
			if (range)
 | 
						|
				range->min_port = list[i].value + 1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
 | 
						|
{
 | 
						|
	bool enumerate_icmp = false;
 | 
						|
 | 
						|
	if (api->type ==  BGP_PBR_UNDEFINED) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: pbr entry undefined. cancel.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	/* because bgp pbr entry may contain unsupported
 | 
						|
	 * combinations, a message will be displayed here if
 | 
						|
	 * not supported.
 | 
						|
	 * for now, only match/set supported is
 | 
						|
	 * - combination src/dst => redirect nexthop [ + rate]
 | 
						|
	 * - combination src/dst => redirect VRF [ + rate]
 | 
						|
	 * - combination src/dst => drop
 | 
						|
	 * - combination srcport + @IP
 | 
						|
	 */
 | 
						|
	if (api->match_protocol_num > 1) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match protocol operations:multiple protocols ( %d). ignoring.",
 | 
						|
				 api->match_protocol_num);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (api->src_prefix_offset > 0 ||
 | 
						|
	    api->dst_prefix_offset > 0) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match prefix offset:"
 | 
						|
				   "implementation does not support it.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (api->match_protocol_num == 1 &&
 | 
						|
	    api->protocol[0].value != PROTOCOL_UDP &&
 | 
						|
	    api->protocol[0].value != PROTOCOL_ICMP &&
 | 
						|
	    api->protocol[0].value != PROTOCOL_ICMPV6 &&
 | 
						|
	    api->protocol[0].value != PROTOCOL_TCP) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring",
 | 
						|
				   api->match_protocol_num);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match src port operations:too complex. ignoring.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match dst port operations:too complex. ignoring.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (!bgp_pbr_extract_enumerate(api->tcpflags,
 | 
						|
				       api->match_tcpflags_num,
 | 
						|
				       OPERATOR_UNARY_AND |
 | 
						|
				       OPERATOR_UNARY_OR, NULL,
 | 
						|
				       FLOWSPEC_TCP_FLAGS)) {
 | 
						|
		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,
 | 
						|
					       OPERATOR_UNARY_OR, NULL,
 | 
						|
					       FLOWSPEC_ICMP_TYPE)) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match icmp type operations:too complex. ignoring.");
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		enumerate_icmp = true;
 | 
						|
	}
 | 
						|
	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,
 | 
						|
					       OPERATOR_UNARY_OR, NULL,
 | 
						|
					       FLOWSPEC_ICMP_CODE)) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match icmp code operations:too complex. ignoring.");
 | 
						|
			return 0;
 | 
						|
		} else if (api->match_icmp_type_num > 1 &&
 | 
						|
			   !enumerate_icmp) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match icmp code is enumerate, and icmp type is not. too complex. ignoring.");
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match port operations:too complex. ignoring.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (api->match_packet_length_num) {
 | 
						|
		bool ret;
 | 
						|
 | 
						|
		ret = bgp_pbr_extract(api->packet_length,
 | 
						|
				      api->match_packet_length_num, NULL);
 | 
						|
		if (!ret)
 | 
						|
			ret = bgp_pbr_extract_enumerate(api->packet_length,
 | 
						|
						api->match_packet_length_num,
 | 
						|
						OPERATOR_UNARY_OR
 | 
						|
						| OPERATOR_UNARY_AND,
 | 
						|
						NULL, FLOWSPEC_PKT_LEN);
 | 
						|
		if (!ret) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match packet length operations:too complex. ignoring.");
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (api->match_dscp_num) {
 | 
						|
		if (!bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num,
 | 
						|
				OPERATOR_UNARY_OR | OPERATOR_UNARY_AND,
 | 
						|
					       NULL, FLOWSPEC_DSCP)) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match DSCP operations:too complex. ignoring.");
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (api->match_flowlabel_num) {
 | 
						|
		if (api->afi == AFI_IP) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match Flow Label operations:"
 | 
						|
					   "Not for IPv4.");
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		if (!bgp_pbr_extract_enumerate(api->flow_label,
 | 
						|
					       api->match_flowlabel_num,
 | 
						|
				OPERATOR_UNARY_OR | OPERATOR_UNARY_AND,
 | 
						|
					       NULL, FLOWSPEC_FLOW_LABEL)) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match FlowLabel operations:"
 | 
						|
					   "too complex. ignoring.");
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match FlowLabel operations "
 | 
						|
				   "not supported. ignoring.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (api->match_fragment_num) {
 | 
						|
		char fail_str[64];
 | 
						|
		bool success;
 | 
						|
 | 
						|
		success = bgp_pbr_extract_enumerate(api->fragment,
 | 
						|
						    api->match_fragment_num,
 | 
						|
						    OPERATOR_UNARY_OR
 | 
						|
						    | OPERATOR_UNARY_AND,
 | 
						|
						    NULL, FLOWSPEC_FRAGMENT);
 | 
						|
		if (success) {
 | 
						|
			int i;
 | 
						|
 | 
						|
			for (i = 0; i < api->match_fragment_num; i++) {
 | 
						|
				if (api->fragment[i].value != 1 &&
 | 
						|
				    api->fragment[i].value != 2 &&
 | 
						|
				    api->fragment[i].value != 4 &&
 | 
						|
				    api->fragment[i].value != 8) {
 | 
						|
					success = false;
 | 
						|
					snprintf(
 | 
						|
						fail_str, sizeof(fail_str),
 | 
						|
						"Value not valid (%d) for this implementation",
 | 
						|
						api->fragment[i].value);
 | 
						|
				}
 | 
						|
				if (api->afi == AFI_IP6 &&
 | 
						|
				    api->fragment[i].value == 1) {
 | 
						|
					success = false;
 | 
						|
					snprintf(fail_str, sizeof(fail_str),
 | 
						|
						 "IPv6 dont fragment match invalid (%d)",
 | 
						|
						 api->fragment[i].value);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (api->afi == AFI_IP6) {
 | 
						|
				success = false;
 | 
						|
				snprintf(fail_str, sizeof(fail_str),
 | 
						|
					 "%s", IPV6_FRAGMENT_INVALID);
 | 
						|
			}
 | 
						|
		} else
 | 
						|
			snprintf(fail_str, sizeof(fail_str),
 | 
						|
				 "too complex. ignoring");
 | 
						|
		if (!success) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR))
 | 
						|
				zlog_debug("BGP: match fragment operation (%d) %s",
 | 
						|
					   api->match_fragment_num,
 | 
						|
					   fail_str);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* no combinations with both src_port and dst_port
 | 
						|
	 * or port with src_port and dst_port
 | 
						|
	 */
 | 
						|
	if (api->match_src_port_num + api->match_dst_port_num +
 | 
						|
	    api->match_port_num > 3) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match multiple port operations: too complex. ignoring.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if ((api->match_src_port_num || api->match_dst_port_num
 | 
						|
	     || api->match_port_num) && (api->match_icmp_type_num
 | 
						|
					 || api->match_icmp_code_num)) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR))
 | 
						|
			zlog_debug("BGP: match multiple port/imcp operations: too complex. ignoring.");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	/* iprule only supports redirect IP */
 | 
						|
	if (api->type == BGP_PBR_IPRULE) {
 | 
						|
		int i;
 | 
						|
 | 
						|
		for (i = 0; i < api->action_num; i++) {
 | 
						|
			if (api->actions[i].action == ACTION_TRAFFICRATE &&
 | 
						|
			    api->actions[i].u.r.rate == 0) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
					bgp_pbr_print_policy_route(api);
 | 
						|
					zlog_debug("BGP: iprule match actions drop not supported");
 | 
						|
				}
 | 
						|
				return 0;
 | 
						|
			}
 | 
						|
			if (api->actions[i].action == ACTION_MARKING) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
					bgp_pbr_print_policy_route(api);
 | 
						|
					zlog_warn("PBR: iprule set DSCP/Flow Label %u not supported",
 | 
						|
						api->actions[i].u.marking_dscp);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (api->actions[i].action == ACTION_REDIRECT) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
					bgp_pbr_print_policy_route(api);
 | 
						|
					zlog_warn("PBR: iprule redirect VRF %u not supported",
 | 
						|
						api->actions[i].u.redirect_vrf);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	} else if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
 | 
						|
		   !(api->match_bitmask & PREFIX_DST_PRESENT)) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
			bgp_pbr_print_policy_route(api);
 | 
						|
			zlog_debug("BGP: match actions without src or dst address can not operate. ignoring.");
 | 
						|
		}
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* return -1 if build or validation failed */
 | 
						|
 | 
						|
int bgp_pbr_build_and_validate_entry(const struct prefix *p,
 | 
						|
				     struct bgp_path_info *path,
 | 
						|
				     struct bgp_pbr_entry_main *api)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	int i, action_count = 0;
 | 
						|
	struct ecommunity *ecom;
 | 
						|
	struct ecommunity_val *ecom_eval;
 | 
						|
	struct bgp_pbr_entry_action *api_action;
 | 
						|
	struct prefix *src = NULL, *dst = NULL;
 | 
						|
	int valid_prefix = 0;
 | 
						|
	struct bgp_pbr_entry_action *api_action_redirect_ip = NULL;
 | 
						|
	bool discard_action_found = false;
 | 
						|
	afi_t afi = family2afi(p->u.prefix_flowspec.family);
 | 
						|
 | 
						|
	/* extract match from flowspec entries */
 | 
						|
	ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr,
 | 
						|
					    p->u.prefix_flowspec.prefixlen, api, afi);
 | 
						|
	if (ret < 0)
 | 
						|
		return -1;
 | 
						|
	/* extract actiosn from flowspec ecom list */
 | 
						|
	if (path && path->attr->ecommunity) {
 | 
						|
		ecom = path->attr->ecommunity;
 | 
						|
		for (i = 0; i < ecom->size; i++) {
 | 
						|
			ecom_eval = (struct ecommunity_val *)
 | 
						|
				(ecom->val + (i * ECOMMUNITY_SIZE));
 | 
						|
			action_count++;
 | 
						|
			if (action_count > ACTIONS_MAX_NUM) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR_ERROR))
 | 
						|
					flog_err(
 | 
						|
						EC_BGP_FLOWSPEC_PACKET,
 | 
						|
						"%s: %s (max %u)",
 | 
						|
						__func__,
 | 
						|
						FSPEC_ACTION_EXCEED_LIMIT,
 | 
						|
						action_count);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			api_action = &api->actions[action_count - 1];
 | 
						|
 | 
						|
			if ((ecom_eval->val[1] ==
 | 
						|
			     (char)ECOMMUNITY_REDIRECT_VRF) &&
 | 
						|
			    (ecom_eval->val[0] ==
 | 
						|
			     (char)ECOMMUNITY_ENCODE_TRANS_EXP ||
 | 
						|
			     ecom_eval->val[0] ==
 | 
						|
			     (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
 | 
						|
			     ecom_eval->val[0] ==
 | 
						|
			     (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_3)) {
 | 
						|
				struct ecommunity *eckey = ecommunity_new();
 | 
						|
				struct ecommunity_val ecom_copy;
 | 
						|
 | 
						|
				memcpy(&ecom_copy, ecom_eval,
 | 
						|
				       sizeof(struct ecommunity_val));
 | 
						|
				ecom_copy.val[0] &=
 | 
						|
					~ECOMMUNITY_ENCODE_TRANS_EXP;
 | 
						|
				ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
 | 
						|
				ecommunity_add_val(eckey, &ecom_copy,
 | 
						|
						   false, false);
 | 
						|
 | 
						|
				api_action->action = ACTION_REDIRECT;
 | 
						|
				api_action->u.redirect_vrf =
 | 
						|
					get_first_vrf_for_redirect_with_rt(
 | 
						|
								eckey);
 | 
						|
				ecommunity_free(&eckey);
 | 
						|
			} else if ((ecom_eval->val[0] ==
 | 
						|
				    (char)ECOMMUNITY_ENCODE_REDIRECT_IP_NH) &&
 | 
						|
				   (ecom_eval->val[1] ==
 | 
						|
				    (char)ECOMMUNITY_REDIRECT_IP_NH)) {
 | 
						|
				/* in case the 2 ecom present,
 | 
						|
				 * do not overwrite
 | 
						|
				 * draft-ietf-idr-flowspec-redirect
 | 
						|
				 */
 | 
						|
				if (api_action_redirect_ip &&
 | 
						|
				    p->u.prefix_flowspec.family == AF_INET) {
 | 
						|
					if (api_action_redirect_ip->u
 | 
						|
					    .zr.redirect_ip_v4.s_addr
 | 
						|
					    != INADDR_ANY)
 | 
						|
						continue;
 | 
						|
					if (path->attr->nexthop.s_addr
 | 
						|
					    == INADDR_ANY)
 | 
						|
						continue;
 | 
						|
					api_action_redirect_ip->u.zr
 | 
						|
						.redirect_ip_v4.s_addr =
 | 
						|
						path->attr->nexthop.s_addr;
 | 
						|
					api_action_redirect_ip->u.zr.duplicate
 | 
						|
						= ecom_eval->val[7];
 | 
						|
					continue;
 | 
						|
				} else if (api_action_redirect_ip &&
 | 
						|
				    p->u.prefix_flowspec.family == AF_INET6) {
 | 
						|
					if (memcmp(&api_action_redirect_ip->u
 | 
						|
						   .zr.redirect_ip_v6,
 | 
						|
						   &in6addr_any,
 | 
						|
						   sizeof(struct in6_addr)))
 | 
						|
						continue;
 | 
						|
					if (path->attr->mp_nexthop_len == 0 ||
 | 
						|
					    path->attr->mp_nexthop_len ==
 | 
						|
					    BGP_ATTR_NHLEN_IPV4 ||
 | 
						|
					    path->attr->mp_nexthop_len ==
 | 
						|
					    BGP_ATTR_NHLEN_VPNV4)
 | 
						|
						continue;
 | 
						|
					memcpy(&api_action_redirect_ip->u
 | 
						|
					       .zr.redirect_ip_v6,
 | 
						|
					       &path->attr->mp_nexthop_global,
 | 
						|
					       sizeof(struct in6_addr));
 | 
						|
					api_action_redirect_ip->u.zr.duplicate
 | 
						|
						= ecom_eval->val[7];
 | 
						|
					continue;
 | 
						|
				} else if (p->u.prefix_flowspec.family ==
 | 
						|
					   AF_INET) {
 | 
						|
					api_action->action = ACTION_REDIRECT_IP;
 | 
						|
					api_action->u.zr.redirect_ip_v4.s_addr =
 | 
						|
						path->attr->nexthop.s_addr;
 | 
						|
					api_action->u.zr.duplicate =
 | 
						|
						ecom_eval->val[7];
 | 
						|
					api_action_redirect_ip = api_action;
 | 
						|
				} else if (p->u.prefix_flowspec.family ==
 | 
						|
					   AF_INET6) {
 | 
						|
					api_action->action = ACTION_REDIRECT_IP;
 | 
						|
					memcpy(&api_action->u
 | 
						|
					       .zr.redirect_ip_v6,
 | 
						|
					       &path->attr->mp_nexthop_global,
 | 
						|
					       sizeof(struct in6_addr));
 | 
						|
					api_action->u.zr.duplicate
 | 
						|
						= ecom_eval->val[7];
 | 
						|
					api_action_redirect_ip = api_action;
 | 
						|
				}
 | 
						|
			} else if ((ecom_eval->val[0] ==
 | 
						|
				    (char)ECOMMUNITY_ENCODE_IP) &&
 | 
						|
				   (ecom_eval->val[1] ==
 | 
						|
				    (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4)) {
 | 
						|
				/* in case the 2 ecom present,
 | 
						|
				 * overwrite simpson draft
 | 
						|
				 * update redirect ip fields
 | 
						|
				 */
 | 
						|
				if (api_action_redirect_ip) {
 | 
						|
					memcpy(&(api_action_redirect_ip->u
 | 
						|
						 .zr.redirect_ip_v4.s_addr),
 | 
						|
					       (ecom_eval->val+2), 4);
 | 
						|
					api_action_redirect_ip->u
 | 
						|
						.zr.duplicate =
 | 
						|
						ecom_eval->val[7];
 | 
						|
					continue;
 | 
						|
				} else {
 | 
						|
					api_action->action = ACTION_REDIRECT_IP;
 | 
						|
					memcpy(&(api_action->u
 | 
						|
						 .zr.redirect_ip_v4.s_addr),
 | 
						|
					       (ecom_eval->val+2), 4);
 | 
						|
					api_action->u.zr.duplicate =
 | 
						|
						ecom_eval->val[7];
 | 
						|
					api_action_redirect_ip = api_action;
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				if (ecom_eval->val[0] !=
 | 
						|
				    (char)ECOMMUNITY_ENCODE_TRANS_EXP)
 | 
						|
					continue;
 | 
						|
				ret = ecommunity_fill_pbr_action(ecom_eval,
 | 
						|
								 api_action,
 | 
						|
								 afi);
 | 
						|
				if (ret != 0)
 | 
						|
					continue;
 | 
						|
				if ((api_action->action == ACTION_TRAFFICRATE)
 | 
						|
				    && api->actions[i].u.r.rate == 0)
 | 
						|
					discard_action_found = true;
 | 
						|
			}
 | 
						|
			api->action_num++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (path && path->attr && path->attr->ipv6_ecommunity) {
 | 
						|
		struct ecommunity_val_ipv6 *ipv6_ecom_eval;
 | 
						|
 | 
						|
		ecom = path->attr->ipv6_ecommunity;
 | 
						|
		for (i = 0; i < ecom->size; i++) {
 | 
						|
			ipv6_ecom_eval = (struct ecommunity_val_ipv6 *)
 | 
						|
				(ecom->val + (i * ecom->unit_size));
 | 
						|
			action_count++;
 | 
						|
			if (action_count > ACTIONS_MAX_NUM) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR_ERROR))
 | 
						|
					flog_err(
 | 
						|
						EC_BGP_FLOWSPEC_PACKET,
 | 
						|
						"%s: flowspec actions exceeds limit (max %u)",
 | 
						|
						__func__, action_count);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			api_action = &api->actions[action_count - 1];
 | 
						|
			if ((ipv6_ecom_eval->val[1] ==
 | 
						|
			     (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) &&
 | 
						|
			    (ipv6_ecom_eval->val[0] ==
 | 
						|
			     (char)ECOMMUNITY_ENCODE_TRANS_EXP)) {
 | 
						|
				struct ecommunity *eckey = ecommunity_new();
 | 
						|
				struct ecommunity_val_ipv6 ecom_copy;
 | 
						|
 | 
						|
				eckey->unit_size = IPV6_ECOMMUNITY_SIZE;
 | 
						|
				memcpy(&ecom_copy, ipv6_ecom_eval,
 | 
						|
				       sizeof(struct ecommunity_val_ipv6));
 | 
						|
				ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
 | 
						|
				ecommunity_add_val_ipv6(eckey, &ecom_copy,
 | 
						|
							false, false);
 | 
						|
				api_action->action = ACTION_REDIRECT;
 | 
						|
				api_action->u.redirect_vrf =
 | 
						|
					get_first_vrf_for_redirect_with_rt(
 | 
						|
								eckey);
 | 
						|
				ecommunity_free(&eckey);
 | 
						|
				api->action_num++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* if ECOMMUNITY_TRAFFIC_RATE = 0 as action
 | 
						|
	 * then reduce the API action list to that action
 | 
						|
	 */
 | 
						|
	if (api->action_num > 1 && discard_action_found) {
 | 
						|
		api->action_num = 1;
 | 
						|
		memset(&api->actions[0], 0,
 | 
						|
		       sizeof(struct bgp_pbr_entry_action));
 | 
						|
		api->actions[0].action = ACTION_TRAFFICRATE;
 | 
						|
	}
 | 
						|
 | 
						|
	/* validate if incoming matc/action is compatible
 | 
						|
	 * with our policy routing engine
 | 
						|
	 */
 | 
						|
	if (!bgp_pbr_validate_policy_route(api))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/* check inconsistency in the match rule */
 | 
						|
	if (api->match_bitmask & PREFIX_SRC_PRESENT) {
 | 
						|
		src = &api->src_prefix;
 | 
						|
		afi = family2afi(src->family);
 | 
						|
		valid_prefix = 1;
 | 
						|
	}
 | 
						|
	if (api->match_bitmask & PREFIX_DST_PRESENT) {
 | 
						|
		dst = &api->dst_prefix;
 | 
						|
		if (valid_prefix && afi != family2afi(dst->family)) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
				bgp_pbr_print_policy_route(api);
 | 
						|
				zlog_debug("%s: inconsistency: no match for afi src and dst (%u/%u)",
 | 
						|
				     __func__, afi, family2afi(dst->family));
 | 
						|
			}
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_match_entry_free(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match_entry *bpme;
 | 
						|
 | 
						|
	bpme = (struct bgp_pbr_match_entry *)arg;
 | 
						|
 | 
						|
	if (bpme->installed) {
 | 
						|
		bgp_send_pbr_ipset_entry_match(bpme, false);
 | 
						|
		bpme->installed = false;
 | 
						|
		bpme->backpointer = NULL;
 | 
						|
	}
 | 
						|
	XFREE(MTYPE_PBR_MATCH_ENTRY, bpme);
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_match_free(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match *bpm;
 | 
						|
 | 
						|
	bpm = (struct bgp_pbr_match *)arg;
 | 
						|
 | 
						|
	hash_clean(bpm->entry_hash, bgp_pbr_match_entry_free);
 | 
						|
 | 
						|
	if (hashcount(bpm->entry_hash) == 0) {
 | 
						|
		/* delete iptable entry first */
 | 
						|
		/* then delete ipset match */
 | 
						|
		if (bpm->installed) {
 | 
						|
			if (bpm->installed_in_iptable) {
 | 
						|
				bgp_send_pbr_iptable(bpm->action,
 | 
						|
						     bpm, false);
 | 
						|
				bpm->installed_in_iptable = false;
 | 
						|
				bpm->action->refcnt--;
 | 
						|
			}
 | 
						|
			bgp_send_pbr_ipset_match(bpm, false);
 | 
						|
			bpm->installed = false;
 | 
						|
			bpm->action = NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	hash_free(bpm->entry_hash);
 | 
						|
 | 
						|
	XFREE(MTYPE_PBR_MATCH, bpm);
 | 
						|
}
 | 
						|
 | 
						|
static void *bgp_pbr_match_alloc_intern(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match *bpm, *new;
 | 
						|
 | 
						|
	bpm = (struct bgp_pbr_match *)arg;
 | 
						|
 | 
						|
	new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
 | 
						|
	memcpy(new, bpm, sizeof(*bpm));
 | 
						|
 | 
						|
	return new;
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_rule_free(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_rule *bpr;
 | 
						|
 | 
						|
	bpr = (struct bgp_pbr_rule *)arg;
 | 
						|
 | 
						|
	/* delete iprule */
 | 
						|
	if (bpr->installed) {
 | 
						|
		bgp_send_pbr_rule_action(bpr->action, bpr, false);
 | 
						|
		bpr->installed = false;
 | 
						|
		bpr->action->refcnt--;
 | 
						|
		bpr->action = NULL;
 | 
						|
	}
 | 
						|
	XFREE(MTYPE_PBR_RULE, bpr);
 | 
						|
}
 | 
						|
 | 
						|
static void *bgp_pbr_rule_alloc_intern(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_rule *bpr, *new;
 | 
						|
 | 
						|
	bpr = (struct bgp_pbr_rule *)arg;
 | 
						|
 | 
						|
	new = XCALLOC(MTYPE_PBR_RULE, sizeof(*new));
 | 
						|
	memcpy(new, bpr, sizeof(*bpr));
 | 
						|
 | 
						|
	return new;
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_action_free(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_action *bpa;
 | 
						|
 | 
						|
	bpa = (struct bgp_pbr_action *)arg;
 | 
						|
 | 
						|
	if (bpa->refcnt == 0) {
 | 
						|
		if (bpa->installed && bpa->table_id != 0) {
 | 
						|
			bgp_send_pbr_rule_action(bpa, NULL, false);
 | 
						|
			bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
 | 
						|
						   AFI_IP,
 | 
						|
						   bpa->table_id,
 | 
						|
						   false);
 | 
						|
			bpa->installed = false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	XFREE(MTYPE_PBR_ACTION, bpa);
 | 
						|
}
 | 
						|
 | 
						|
static void *bgp_pbr_action_alloc_intern(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_action *bpa, *new;
 | 
						|
 | 
						|
	bpa = (struct bgp_pbr_action *)arg;
 | 
						|
 | 
						|
	new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
 | 
						|
 | 
						|
	memcpy(new, bpa, sizeof(*bpa));
 | 
						|
 | 
						|
	return new;
 | 
						|
}
 | 
						|
 | 
						|
static void *bgp_pbr_match_entry_alloc_intern(void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match_entry *bpme, *new;
 | 
						|
 | 
						|
	bpme = (struct bgp_pbr_match_entry *)arg;
 | 
						|
 | 
						|
	new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
 | 
						|
 | 
						|
	memcpy(new, bpme, sizeof(*bpme));
 | 
						|
 | 
						|
	return new;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t bgp_pbr_match_hash_key(const void *arg)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_match *pbm = arg;
 | 
						|
	uint32_t key;
 | 
						|
 | 
						|
	key = jhash_1word(pbm->vrf_id, 0x4312abde);
 | 
						|
	key = jhash_1word(pbm->flags, key);
 | 
						|
	key = jhash_1word(pbm->family, key);
 | 
						|
	key = jhash(&pbm->pkt_len_min, 2, key);
 | 
						|
	key = jhash(&pbm->pkt_len_max, 2, key);
 | 
						|
	key = jhash(&pbm->tcp_flags, 2, key);
 | 
						|
	key = jhash(&pbm->tcp_mask_flags, 2, key);
 | 
						|
	key = jhash(&pbm->dscp_value, 1, key);
 | 
						|
	key = jhash(&pbm->flow_label, 2, key);
 | 
						|
	key = jhash(&pbm->fragment, 1, key);
 | 
						|
	key = jhash(&pbm->protocol, 1, key);
 | 
						|
	return jhash_1word(pbm->type, key);
 | 
						|
}
 | 
						|
 | 
						|
bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_match *r1, *r2;
 | 
						|
 | 
						|
	r1 = (const struct bgp_pbr_match *)arg1;
 | 
						|
	r2 = (const struct bgp_pbr_match *)arg2;
 | 
						|
 | 
						|
	if (r1->vrf_id != r2->vrf_id)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->family != r2->family)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->type != r2->type)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->flags != r2->flags)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->action != r2->action)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->pkt_len_min != r2->pkt_len_min)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->pkt_len_max != r2->pkt_len_max)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->tcp_flags != r2->tcp_flags)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->tcp_mask_flags != r2->tcp_mask_flags)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->dscp_value != r2->dscp_value)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->flow_label != r2->flow_label)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->fragment != r2->fragment)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->protocol != r2->protocol)
 | 
						|
		return false;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t bgp_pbr_rule_hash_key(const void *arg)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_rule *pbr = arg;
 | 
						|
	uint32_t key;
 | 
						|
 | 
						|
	key = prefix_hash_key(&pbr->src);
 | 
						|
	key = jhash_1word(pbr->vrf_id, key);
 | 
						|
	key = jhash_1word(pbr->flags, key);
 | 
						|
	return jhash_1word(prefix_hash_key(&pbr->dst), key);
 | 
						|
}
 | 
						|
 | 
						|
bool bgp_pbr_rule_hash_equal(const void *arg1, const void *arg2)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_rule *r1, *r2;
 | 
						|
 | 
						|
	r1 = (const struct bgp_pbr_rule *)arg1;
 | 
						|
	r2 = (const struct bgp_pbr_rule *)arg2;
 | 
						|
 | 
						|
	if (r1->vrf_id != r2->vrf_id)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->flags != r2->flags)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->action != r2->action)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if ((r1->flags & MATCH_IP_SRC_SET) &&
 | 
						|
	    !prefix_same(&r1->src, &r2->src))
 | 
						|
		return false;
 | 
						|
 | 
						|
	if ((r1->flags & MATCH_IP_DST_SET) &&
 | 
						|
	    !prefix_same(&r1->dst, &r2->dst))
 | 
						|
		return false;
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t bgp_pbr_match_entry_hash_key(const void *arg)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_match_entry *pbme;
 | 
						|
	uint32_t key;
 | 
						|
 | 
						|
	pbme = arg;
 | 
						|
	key = prefix_hash_key(&pbme->src);
 | 
						|
	key = jhash_1word(prefix_hash_key(&pbme->dst), key);
 | 
						|
	key = jhash(&pbme->dst_port_min, 2, key);
 | 
						|
	key = jhash(&pbme->src_port_min, 2, key);
 | 
						|
	key = jhash(&pbme->dst_port_max, 2, key);
 | 
						|
	key = jhash(&pbme->src_port_max, 2, key);
 | 
						|
	key = jhash(&pbme->proto, 1, key);
 | 
						|
 | 
						|
	return key;
 | 
						|
}
 | 
						|
 | 
						|
bool bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_match_entry *r1, *r2;
 | 
						|
 | 
						|
	r1 = (const struct bgp_pbr_match_entry *)arg1;
 | 
						|
	r2 = (const struct bgp_pbr_match_entry *)arg2;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * on updates, comparing backpointer is not necessary
 | 
						|
	 * unique value is self calculated
 | 
						|
	 * rate is ignored for now
 | 
						|
	 */
 | 
						|
 | 
						|
	if (!prefix_same(&r1->src, &r2->src))
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (!prefix_same(&r1->dst, &r2->dst))
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->src_port_min != r2->src_port_min)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->dst_port_min != r2->dst_port_min)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->src_port_max != r2->src_port_max)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->dst_port_max != r2->dst_port_max)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->proto != r2->proto)
 | 
						|
		return false;
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t bgp_pbr_action_hash_key(const void *arg)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_action *pbra;
 | 
						|
	uint32_t key;
 | 
						|
 | 
						|
	pbra = arg;
 | 
						|
	key = jhash_1word(pbra->table_id, 0x4312abde);
 | 
						|
	key = jhash_1word(pbra->fwmark, key);
 | 
						|
	key = jhash_1word(pbra->afi, key);
 | 
						|
	return key;
 | 
						|
}
 | 
						|
 | 
						|
bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
 | 
						|
{
 | 
						|
	const struct bgp_pbr_action *r1, *r2;
 | 
						|
 | 
						|
	r1 = (const struct bgp_pbr_action *)arg1;
 | 
						|
	r2 = (const struct bgp_pbr_action *)arg2;
 | 
						|
 | 
						|
	/* unique value is self calculated
 | 
						|
	 * table and fwmark is self calculated
 | 
						|
	 * rate is ignored
 | 
						|
	 */
 | 
						|
	if (r1->vrf_id != r2->vrf_id)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (r1->afi != r2->afi)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
 | 
						|
		return false;
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id,
 | 
						|
					 uint32_t unique)
 | 
						|
{
 | 
						|
	struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
 | 
						|
	struct bgp_pbr_rule_unique bpru;
 | 
						|
 | 
						|
	if (!bgp || unique == 0)
 | 
						|
		return NULL;
 | 
						|
	bpru.unique = unique;
 | 
						|
	bpru.bpr_found = NULL;
 | 
						|
	hash_walk(bgp->pbr_rule_hash, bgp_pbr_rule_walkcb, &bpru);
 | 
						|
	return bpru.bpr_found;
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
 | 
						|
						  uint32_t unique)
 | 
						|
{
 | 
						|
	struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
 | 
						|
	struct bgp_pbr_action_unique bpau;
 | 
						|
 | 
						|
	if (!bgp || unique == 0)
 | 
						|
		return NULL;
 | 
						|
	bpau.unique = unique;
 | 
						|
	bpau.bpa_found = NULL;
 | 
						|
	hash_walk(bgp->pbr_action_hash, bgp_pbr_action_walkcb, &bpau);
 | 
						|
	return bpau.bpa_found;
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
 | 
						|
						 uint32_t unique)
 | 
						|
{
 | 
						|
	struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
 | 
						|
	struct bgp_pbr_match_unique bpmu;
 | 
						|
 | 
						|
	if (!bgp || unique == 0)
 | 
						|
		return NULL;
 | 
						|
	bpmu.unique = unique;
 | 
						|
	bpmu.bpm_found = NULL;
 | 
						|
	hash_walk(bgp->pbr_match_hash, bgp_pbr_match_walkcb, &bpmu);
 | 
						|
	return bpmu.bpm_found;
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id,
 | 
						|
						       char *ipset_name,
 | 
						|
						       uint32_t unique)
 | 
						|
{
 | 
						|
	struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
 | 
						|
	struct bgp_pbr_match_entry_unique bpmeu;
 | 
						|
	struct bgp_pbr_match_ipsetname bpmi;
 | 
						|
 | 
						|
	if (!bgp || unique == 0)
 | 
						|
		return NULL;
 | 
						|
	bpmi.ipsetname = XCALLOC(MTYPE_TMP, ZEBRA_IPSET_NAME_SIZE);
 | 
						|
	snprintf(bpmi.ipsetname, ZEBRA_IPSET_NAME_SIZE, "%s", ipset_name);
 | 
						|
	bpmi.bpm_found = NULL;
 | 
						|
	hash_walk(bgp->pbr_match_hash, bgp_pbr_match_pername_walkcb, &bpmi);
 | 
						|
	XFREE(MTYPE_TMP, bpmi.ipsetname);
 | 
						|
	if (!bpmi.bpm_found)
 | 
						|
		return NULL;
 | 
						|
	bpmeu.bpme_found = NULL;
 | 
						|
	bpmeu.unique = unique;
 | 
						|
	hash_walk(bpmi.bpm_found->entry_hash,
 | 
						|
		  bgp_pbr_match_entry_walkcb, &bpmeu);
 | 
						|
	return bpmeu.bpme_found;
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
 | 
						|
						   uint32_t unique)
 | 
						|
{
 | 
						|
	struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
 | 
						|
	struct bgp_pbr_match_iptable_unique bpmiu;
 | 
						|
 | 
						|
	if (!bgp || unique == 0)
 | 
						|
		return NULL;
 | 
						|
	bpmiu.unique = unique;
 | 
						|
	bpmiu.bpm_found = NULL;
 | 
						|
	hash_walk(bgp->pbr_match_hash, bgp_pbr_match_iptable_walkcb, &bpmiu);
 | 
						|
	return bpmiu.bpm_found;
 | 
						|
}
 | 
						|
 | 
						|
void bgp_pbr_cleanup(struct bgp *bgp)
 | 
						|
{
 | 
						|
	if (bgp->pbr_match_hash) {
 | 
						|
		hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free);
 | 
						|
		hash_free(bgp->pbr_match_hash);
 | 
						|
		bgp->pbr_match_hash = NULL;
 | 
						|
	}
 | 
						|
	if (bgp->pbr_rule_hash) {
 | 
						|
		hash_clean(bgp->pbr_rule_hash, bgp_pbr_rule_free);
 | 
						|
		hash_free(bgp->pbr_rule_hash);
 | 
						|
		bgp->pbr_rule_hash = NULL;
 | 
						|
	}
 | 
						|
	if (bgp->pbr_action_hash) {
 | 
						|
		hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free);
 | 
						|
		hash_free(bgp->pbr_action_hash);
 | 
						|
		bgp->pbr_action_hash = NULL;
 | 
						|
	}
 | 
						|
	if (bgp->bgp_pbr_cfg == NULL)
 | 
						|
		return;
 | 
						|
	bgp_pbr_reset(bgp, AFI_IP);
 | 
						|
	bgp_pbr_reset(bgp, AFI_IP6);
 | 
						|
	XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg);
 | 
						|
}
 | 
						|
 | 
						|
void bgp_pbr_init(struct bgp *bgp)
 | 
						|
{
 | 
						|
	bgp->pbr_match_hash =
 | 
						|
		hash_create_size(8, bgp_pbr_match_hash_key,
 | 
						|
				 bgp_pbr_match_hash_equal,
 | 
						|
				 "Match Hash");
 | 
						|
	bgp->pbr_action_hash =
 | 
						|
		hash_create_size(8, bgp_pbr_action_hash_key,
 | 
						|
				 bgp_pbr_action_hash_equal,
 | 
						|
				 "Match Hash Entry");
 | 
						|
 | 
						|
	bgp->pbr_rule_hash =
 | 
						|
		hash_create_size(8, bgp_pbr_rule_hash_key,
 | 
						|
				 bgp_pbr_rule_hash_equal,
 | 
						|
				 "Match Rule");
 | 
						|
 | 
						|
	bgp->bgp_pbr_cfg = XCALLOC(MTYPE_PBR, sizeof(struct bgp_pbr_config));
 | 
						|
	bgp->bgp_pbr_cfg->pbr_interface_any_ipv4 = true;
 | 
						|
}
 | 
						|
 | 
						|
void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
 | 
						|
{
 | 
						|
	int i = 0;
 | 
						|
	char return_string[512];
 | 
						|
	char *ptr = return_string;
 | 
						|
	char buff[64];
 | 
						|
	int nb_items = 0;
 | 
						|
	int delta, len = sizeof(return_string);
 | 
						|
 | 
						|
	delta = snprintf(ptr, sizeof(return_string),  "MATCH : ");
 | 
						|
	len -= delta;
 | 
						|
	ptr += delta;
 | 
						|
	if (api->match_bitmask & PREFIX_SRC_PRESENT) {
 | 
						|
		struct prefix *p = &(api->src_prefix);
 | 
						|
 | 
						|
		if (api->src_prefix_offset)
 | 
						|
			delta = snprintf(ptr, len, "@src %s/off%u",
 | 
						|
				       prefix2str(p, buff, 64),
 | 
						|
				       api->src_prefix_offset);
 | 
						|
		else
 | 
						|
			delta = snprintf(ptr, len, "@src %s",
 | 
						|
				       prefix2str(p, buff, 64));
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	}
 | 
						|
	if (api->match_bitmask & PREFIX_DST_PRESENT) {
 | 
						|
		struct prefix *p = &(api->dst_prefix);
 | 
						|
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
		if (api->dst_prefix_offset)
 | 
						|
			delta = snprintf(ptr, len, "@dst %s/off%u",
 | 
						|
				       prefix2str(p, buff, 64),
 | 
						|
				       api->dst_prefix_offset);
 | 
						|
		else
 | 
						|
			delta = snprintf(ptr, len, "@dst %s",
 | 
						|
					 prefix2str(p, buff, 64));
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_protocol_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_protocol_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->protocol[i],
 | 
						|
						   i > 0 ? NULL : "@proto ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_src_port_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_src_port_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->src_port[i],
 | 
						|
						   i > 0 ? NULL : "@srcport ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_dst_port_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_dst_port_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dst_port[i],
 | 
						|
						   i > 0 ? NULL : "@dstport ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_port_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_port_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->port[i],
 | 
						|
						   i > 0 ? NULL : "@port ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_icmp_type_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_icmp_type_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_type[i],
 | 
						|
						   i > 0 ? NULL : "@icmptype ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_icmp_code_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_icmp_code_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_code[i],
 | 
						|
						   i > 0 ? NULL : "@icmpcode ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_packet_length_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_packet_length_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len,
 | 
						|
						   &api->packet_length[i],
 | 
						|
						   i > 0 ? NULL : "@plen ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_dscp_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_dscp_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dscp[i],
 | 
						|
						   i > 0 ? NULL : "@dscp ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_flowlabel_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_flowlabel_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len,
 | 
						|
						  &api->flow_label[i],
 | 
						|
						  i > 0 ? NULL : "@flowlabel ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_tcpflags_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_tcpflags_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->tcpflags[i],
 | 
						|
						   i > 0 ? NULL : "@tcpflags ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_fragment_num)
 | 
						|
		INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
	for (i = 0; i < api->match_fragment_num; i++) {
 | 
						|
		delta = snprintf_bgp_pbr_match_val(ptr, len, &api->fragment[i],
 | 
						|
						   i > 0 ? NULL : "@fragment ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
 | 
						|
	len = sizeof(return_string);
 | 
						|
	if (!nb_items) {
 | 
						|
		ptr = return_string;
 | 
						|
	} else {
 | 
						|
		len -= (ptr - return_string);
 | 
						|
		delta = snprintf(ptr, len, "; ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
	if (api->action_num) {
 | 
						|
		delta = snprintf(ptr, len, "SET : ");
 | 
						|
		len -= delta;
 | 
						|
		ptr += delta;
 | 
						|
	}
 | 
						|
	nb_items = 0;
 | 
						|
	for (i = 0; i < api->action_num; i++) {
 | 
						|
		switch (api->actions[i].action) {
 | 
						|
		case ACTION_TRAFFICRATE:
 | 
						|
			INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
			delta = snprintf(ptr, len, "@set rate %f",
 | 
						|
					api->actions[i].u.r.rate);
 | 
						|
			len -= delta;
 | 
						|
			ptr += delta;
 | 
						|
			break;
 | 
						|
		case ACTION_TRAFFIC_ACTION:
 | 
						|
			INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
			delta = snprintf(ptr, len, "@action ");
 | 
						|
			len -= delta;
 | 
						|
			ptr += delta;
 | 
						|
			if (api->actions[i].u.za.filter
 | 
						|
			    & TRAFFIC_ACTION_TERMINATE) {
 | 
						|
				delta = snprintf(ptr, len,
 | 
						|
						 " terminate (apply filter(s))");
 | 
						|
				len -= delta;
 | 
						|
				ptr += delta;
 | 
						|
			}
 | 
						|
			if (api->actions[i].u.za.filter
 | 
						|
			    & TRAFFIC_ACTION_DISTRIBUTE) {
 | 
						|
				delta = snprintf(ptr, len, " distribute");
 | 
						|
				len -= delta;
 | 
						|
				ptr += delta;
 | 
						|
			}
 | 
						|
			if (api->actions[i].u.za.filter
 | 
						|
			    & TRAFFIC_ACTION_SAMPLE) {
 | 
						|
				delta = snprintf(ptr, len, " sample");
 | 
						|
				len -= delta;
 | 
						|
				ptr += delta;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case ACTION_REDIRECT_IP: {
 | 
						|
			char local_buff[INET6_ADDRSTRLEN];
 | 
						|
			void *ptr_ip;
 | 
						|
 | 
						|
			INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
			if (api->afi == AF_INET)
 | 
						|
				ptr_ip = &api->actions[i].u.zr.redirect_ip_v4;
 | 
						|
			else
 | 
						|
				ptr_ip = &api->actions[i].u.zr.redirect_ip_v6;
 | 
						|
			if (inet_ntop(afi2family(api->afi),
 | 
						|
				      ptr_ip, local_buff,
 | 
						|
				      INET6_ADDRSTRLEN) != NULL) {
 | 
						|
				delta = snprintf(ptr, len,
 | 
						|
					  "@redirect ip nh %s", local_buff);
 | 
						|
				len -= delta;
 | 
						|
				ptr += delta;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		case ACTION_REDIRECT: {
 | 
						|
			struct vrf *vrf;
 | 
						|
 | 
						|
			vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf);
 | 
						|
			INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
			delta = snprintf(ptr, len, "@redirect vrf %s(%u)",
 | 
						|
					 VRF_LOGNAME(vrf),
 | 
						|
					 api->actions[i].u.redirect_vrf);
 | 
						|
			len -= delta;
 | 
						|
			ptr += delta;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		case ACTION_MARKING:
 | 
						|
			INCREMENT_DISPLAY(ptr, nb_items, len);
 | 
						|
			delta = snprintf(ptr, len, "@set dscp/flowlabel %u",
 | 
						|
					 api->actions[i].u.marking_dscp);
 | 
						|
			len -= delta;
 | 
						|
			ptr += delta;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	zlog_info("%s", return_string);
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_flush_iprule(struct bgp *bgp, struct bgp_pbr_action *bpa,
 | 
						|
				  struct bgp_pbr_rule *bpr)
 | 
						|
{
 | 
						|
	/* if bpr is null, do nothing
 | 
						|
	 */
 | 
						|
	if (bpr == NULL)
 | 
						|
		return;
 | 
						|
	if (bpr->installed) {
 | 
						|
		bgp_send_pbr_rule_action(bpa, bpr, false);
 | 
						|
		bpr->installed = false;
 | 
						|
		bpr->action->refcnt--;
 | 
						|
		bpr->action = NULL;
 | 
						|
		if (bpr->path) {
 | 
						|
			struct bgp_path_info *path;
 | 
						|
			struct bgp_path_info_extra *extra;
 | 
						|
 | 
						|
			/* unlink path to bpme */
 | 
						|
			path = (struct bgp_path_info *)bpr->path;
 | 
						|
			extra = bgp_path_info_extra_get(path);
 | 
						|
			if (extra->bgp_fs_iprule)
 | 
						|
				listnode_delete(extra->bgp_fs_iprule, bpr);
 | 
						|
			bpr->path = NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	hash_release(bgp->pbr_rule_hash, bpr);
 | 
						|
	if (bpa->refcnt == 0) {
 | 
						|
		if (bpa->installed && bpa->table_id != 0) {
 | 
						|
			bgp_send_pbr_rule_action(bpa, NULL, false);
 | 
						|
			bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
 | 
						|
						   AFI_IP,
 | 
						|
						   bpa->table_id,
 | 
						|
						   false);
 | 
						|
			bpa->installed = false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
 | 
						|
				struct bgp_pbr_match *bpm,
 | 
						|
				struct bgp_pbr_match_entry *bpme)
 | 
						|
{
 | 
						|
	/* if bpme is null, bpm is also null
 | 
						|
	 */
 | 
						|
	if (bpme == NULL)
 | 
						|
		return;
 | 
						|
	/* ipset del entry */
 | 
						|
	if (bpme->installed) {
 | 
						|
		bgp_send_pbr_ipset_entry_match(bpme, false);
 | 
						|
		bpme->installed = false;
 | 
						|
		bpme->backpointer = NULL;
 | 
						|
		if (bpme->path) {
 | 
						|
			struct bgp_path_info *path;
 | 
						|
			struct bgp_path_info_extra *extra;
 | 
						|
 | 
						|
			/* unlink path to bpme */
 | 
						|
			path = (struct bgp_path_info *)bpme->path;
 | 
						|
			extra = bgp_path_info_extra_get(path);
 | 
						|
			if (extra->bgp_fs_pbr)
 | 
						|
				listnode_delete(extra->bgp_fs_pbr, bpme);
 | 
						|
			bpme->path = NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	hash_release(bpm->entry_hash, bpme);
 | 
						|
	if (hashcount(bpm->entry_hash) == 0) {
 | 
						|
		/* delete iptable entry first */
 | 
						|
		/* then delete ipset match */
 | 
						|
		if (bpm->installed) {
 | 
						|
			if (bpm->installed_in_iptable) {
 | 
						|
				bgp_send_pbr_iptable(bpm->action,
 | 
						|
						     bpm, false);
 | 
						|
				bpm->installed_in_iptable = false;
 | 
						|
				bpm->action->refcnt--;
 | 
						|
			}
 | 
						|
			bgp_send_pbr_ipset_match(bpm, false);
 | 
						|
			bpm->installed = false;
 | 
						|
			bpm->action = NULL;
 | 
						|
		}
 | 
						|
		hash_release(bgp->pbr_match_hash, bpm);
 | 
						|
		/* XXX release pbr_match_action if not used
 | 
						|
		 * note that drop does not need to call send_pbr_action
 | 
						|
		 */
 | 
						|
	}
 | 
						|
	if (bpa->refcnt == 0) {
 | 
						|
		if (bpa->installed && bpa->table_id != 0) {
 | 
						|
			bgp_send_pbr_rule_action(bpa, NULL, false);
 | 
						|
			bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
 | 
						|
						   bpa->afi,
 | 
						|
						   bpa->table_id,
 | 
						|
						   false);
 | 
						|
			bpa->installed = false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_match_entry_remain {
 | 
						|
	struct bgp_pbr_match_entry *bpme_to_match;
 | 
						|
	struct bgp_pbr_match_entry *bpme_found;
 | 
						|
};
 | 
						|
 | 
						|
struct bgp_pbr_rule_remain {
 | 
						|
	struct bgp_pbr_rule *bpr_to_match;
 | 
						|
	struct bgp_pbr_rule *bpr_found;
 | 
						|
};
 | 
						|
 | 
						|
static int bgp_pbr_get_same_rule(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_rule *r1 = (struct bgp_pbr_rule *)bucket->data;
 | 
						|
	struct bgp_pbr_rule_remain *ctxt =
 | 
						|
		(struct bgp_pbr_rule_remain *)arg;
 | 
						|
	struct bgp_pbr_rule *r2;
 | 
						|
 | 
						|
	r2 = ctxt->bpr_to_match;
 | 
						|
 | 
						|
	if (r1->vrf_id != r2->vrf_id)
 | 
						|
		return HASHWALK_CONTINUE;
 | 
						|
 | 
						|
	if (r1->flags != r2->flags)
 | 
						|
		return HASHWALK_CONTINUE;
 | 
						|
 | 
						|
	if ((r1->flags & MATCH_IP_SRC_SET) &&
 | 
						|
	    !prefix_same(&r1->src, &r2->src))
 | 
						|
		return HASHWALK_CONTINUE;
 | 
						|
 | 
						|
	if ((r1->flags & MATCH_IP_DST_SET) &&
 | 
						|
	    !prefix_same(&r1->dst, &r2->dst))
 | 
						|
		return HASHWALK_CONTINUE;
 | 
						|
 | 
						|
	/* this function is used for two cases:
 | 
						|
	 * - remove an entry upon withdraw request
 | 
						|
	 * (case r2->action is null)
 | 
						|
	 * - replace an old iprule with different action
 | 
						|
	 * (case r2->action is != null)
 | 
						|
	 * the old one is removed after the new one
 | 
						|
	 * this is to avoid disruption in traffic
 | 
						|
	 */
 | 
						|
	if (r2->action == NULL ||
 | 
						|
	    r1->action != r2->action) {
 | 
						|
		ctxt->bpr_found = r1;
 | 
						|
		return HASHWALK_ABORT;
 | 
						|
	}
 | 
						|
	return HASHWALK_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static int bgp_pbr_get_remaining_entry(struct hash_bucket *bucket, void *arg)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
 | 
						|
	struct bgp_pbr_match_entry_remain *bpmer =
 | 
						|
		(struct bgp_pbr_match_entry_remain *)arg;
 | 
						|
	struct bgp_pbr_match *bpm_temp;
 | 
						|
	struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
 | 
						|
 | 
						|
	if (!bpme->backpointer ||
 | 
						|
	    bpm == bpme->backpointer ||
 | 
						|
	    bpme->backpointer->action == bpm->action)
 | 
						|
		return HASHWALK_CONTINUE;
 | 
						|
	/* ensure bpm other characteristics are equal */
 | 
						|
	bpm_temp = bpme->backpointer;
 | 
						|
	if (bpm_temp->vrf_id != bpm->vrf_id ||
 | 
						|
	    bpm_temp->type != bpm->type ||
 | 
						|
	    bpm_temp->flags != bpm->flags ||
 | 
						|
	    bpm_temp->tcp_flags != bpm->tcp_flags ||
 | 
						|
	    bpm_temp->tcp_mask_flags != bpm->tcp_mask_flags ||
 | 
						|
	    bpm_temp->pkt_len_min != bpm->pkt_len_min ||
 | 
						|
	    bpm_temp->pkt_len_max != bpm->pkt_len_max ||
 | 
						|
	    bpm_temp->dscp_value != bpm->dscp_value ||
 | 
						|
	    bpm_temp->flow_label != bpm->flow_label ||
 | 
						|
	    bpm_temp->family != bpm->family ||
 | 
						|
	    bpm_temp->fragment != bpm->fragment)
 | 
						|
		return HASHWALK_CONTINUE;
 | 
						|
 | 
						|
	/* look for remaining bpme */
 | 
						|
	bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
 | 
						|
	if (!bpmer->bpme_found)
 | 
						|
		return HASHWALK_CONTINUE;
 | 
						|
	return HASHWALK_ABORT;
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_policyroute_remove_from_zebra_unit(
 | 
						|
	struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match temp;
 | 
						|
	struct bgp_pbr_match_entry temp2;
 | 
						|
	struct bgp_pbr_rule pbr_rule;
 | 
						|
	struct bgp_pbr_rule *bpr;
 | 
						|
	struct bgp_pbr_match *bpm;
 | 
						|
	struct bgp_pbr_match_entry *bpme;
 | 
						|
	struct bgp_pbr_match_entry_remain bpmer;
 | 
						|
	struct bgp_pbr_range_port *src_port;
 | 
						|
	struct bgp_pbr_range_port *dst_port;
 | 
						|
	struct bgp_pbr_range_port *pkt_len;
 | 
						|
	struct bgp_pbr_rule_remain bprr;
 | 
						|
 | 
						|
	if (!bpf)
 | 
						|
		return;
 | 
						|
	src_port = bpf->src_port;
 | 
						|
	dst_port = bpf->dst_port;
 | 
						|
	pkt_len = bpf->pkt_len;
 | 
						|
 | 
						|
	if (BGP_DEBUG(zebra, ZEBRA))
 | 
						|
		bgp_pbr_dump_entry(bpf, false);
 | 
						|
 | 
						|
	/* as we don't know information from EC
 | 
						|
	 * look for bpm that have the bpm
 | 
						|
	 * with vrf_id characteristics
 | 
						|
	 */
 | 
						|
	memset(&temp2, 0, sizeof(temp2));
 | 
						|
	memset(&temp, 0, sizeof(temp));
 | 
						|
 | 
						|
	if (bpf->type == BGP_PBR_IPRULE) {
 | 
						|
		memset(&pbr_rule, 0, sizeof(pbr_rule));
 | 
						|
		pbr_rule.vrf_id = bpf->vrf_id;
 | 
						|
		if (bpf->src) {
 | 
						|
			prefix_copy(&pbr_rule.src, bpf->src);
 | 
						|
			pbr_rule.flags |= MATCH_IP_SRC_SET;
 | 
						|
		}
 | 
						|
		if (bpf->dst) {
 | 
						|
			prefix_copy(&pbr_rule.dst, bpf->dst);
 | 
						|
			pbr_rule.flags |= MATCH_IP_DST_SET;
 | 
						|
		}
 | 
						|
		bpr = &pbr_rule;
 | 
						|
		/* A previous entry may already exist
 | 
						|
		 * flush previous entry if necessary
 | 
						|
		 */
 | 
						|
		bprr.bpr_to_match = bpr;
 | 
						|
		bprr.bpr_found = NULL;
 | 
						|
		hash_walk(bgp->pbr_rule_hash, bgp_pbr_get_same_rule, &bprr);
 | 
						|
		if (bprr.bpr_found) {
 | 
						|
			static struct bgp_pbr_rule *local_bpr;
 | 
						|
			static struct bgp_pbr_action *local_bpa;
 | 
						|
 | 
						|
			local_bpr = bprr.bpr_found;
 | 
						|
			local_bpa = local_bpr->action;
 | 
						|
			bgp_pbr_flush_iprule(bgp, local_bpa,
 | 
						|
					     local_bpr);
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	temp.family = bpf->family;
 | 
						|
	if (bpf->src) {
 | 
						|
		temp.flags |= MATCH_IP_SRC_SET;
 | 
						|
		prefix_copy(&temp2.src, bpf->src);
 | 
						|
	} else
 | 
						|
		temp2.src.family = bpf->family;
 | 
						|
	if (bpf->dst) {
 | 
						|
		temp.flags |= MATCH_IP_DST_SET;
 | 
						|
		prefix_copy(&temp2.dst, bpf->dst);
 | 
						|
	} else
 | 
						|
		temp2.dst.family = bpf->family;
 | 
						|
	if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
 | 
						|
		if (bpf->protocol == IPPROTO_ICMP)
 | 
						|
			temp.flags |= MATCH_ICMP_SET;
 | 
						|
		temp.flags |= MATCH_PORT_SRC_SET;
 | 
						|
		temp2.src_port_min = src_port->min_port;
 | 
						|
		if (src_port->max_port) {
 | 
						|
			temp.flags |= MATCH_PORT_SRC_RANGE_SET;
 | 
						|
			temp2.src_port_max = src_port->max_port;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
 | 
						|
		if (bpf->protocol == IPPROTO_ICMP)
 | 
						|
			temp.flags |= MATCH_ICMP_SET;
 | 
						|
		temp.flags |= MATCH_PORT_DST_SET;
 | 
						|
		temp2.dst_port_min = dst_port->min_port;
 | 
						|
		if (dst_port->max_port) {
 | 
						|
			temp.flags |= MATCH_PORT_DST_RANGE_SET;
 | 
						|
			temp2.dst_port_max = dst_port->max_port;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	temp2.proto = bpf->protocol;
 | 
						|
 | 
						|
	if (pkt_len) {
 | 
						|
		temp.pkt_len_min = pkt_len->min_port;
 | 
						|
		if (pkt_len->max_port)
 | 
						|
			temp.pkt_len_max = pkt_len->max_port;
 | 
						|
	} else if (bpf->pkt_len_val) {
 | 
						|
		if (bpf->pkt_len_val->mask)
 | 
						|
			temp.flags |= MATCH_PKT_LEN_INVERSE_SET;
 | 
						|
		temp.pkt_len_min = bpf->pkt_len_val->val;
 | 
						|
	}
 | 
						|
	if (bpf->tcp_flags) {
 | 
						|
		temp.tcp_flags = bpf->tcp_flags->val;
 | 
						|
		temp.tcp_mask_flags = bpf->tcp_flags->mask;
 | 
						|
	}
 | 
						|
	if (bpf->dscp) {
 | 
						|
		if (bpf->dscp->mask)
 | 
						|
			temp.flags |= MATCH_DSCP_INVERSE_SET;
 | 
						|
		else
 | 
						|
			temp.flags |= MATCH_DSCP_SET;
 | 
						|
		temp.dscp_value = bpf->dscp->val;
 | 
						|
	}
 | 
						|
	if (bpf->flow_label) {
 | 
						|
		if (bpf->flow_label->mask)
 | 
						|
			temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET;
 | 
						|
		else
 | 
						|
			temp.flags |= MATCH_FLOW_LABEL_SET;
 | 
						|
		temp.flow_label = bpf->flow_label->val;
 | 
						|
	}
 | 
						|
 | 
						|
	if (bpf->fragment) {
 | 
						|
		if (bpf->fragment->mask)
 | 
						|
			temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
 | 
						|
		temp.fragment = bpf->fragment->val;
 | 
						|
	}
 | 
						|
 | 
						|
	if (bpf->src == NULL || bpf->dst == NULL) {
 | 
						|
		if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
 | 
						|
			temp.type = IPSET_NET_PORT;
 | 
						|
		else
 | 
						|
			temp.type = IPSET_NET;
 | 
						|
	} else {
 | 
						|
		if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
 | 
						|
			temp.type = IPSET_NET_PORT_NET;
 | 
						|
		else
 | 
						|
			temp.type = IPSET_NET_NET;
 | 
						|
	}
 | 
						|
	if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
 | 
						|
		temp.vrf_id = VRF_DEFAULT;
 | 
						|
	else
 | 
						|
		temp.vrf_id = bpf->vrf_id;
 | 
						|
	bpme = &temp2;
 | 
						|
	bpm = &temp;
 | 
						|
	bpme->backpointer = bpm;
 | 
						|
	/* right now, a previous entry may already exist
 | 
						|
	 * flush previous entry if necessary
 | 
						|
	 */
 | 
						|
	bpmer.bpme_to_match = bpme;
 | 
						|
	bpmer.bpme_found = NULL;
 | 
						|
	hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
 | 
						|
	if (bpmer.bpme_found) {
 | 
						|
		static struct bgp_pbr_match *local_bpm;
 | 
						|
		static struct bgp_pbr_action *local_bpa;
 | 
						|
 | 
						|
		local_bpm = bpmer.bpme_found->backpointer;
 | 
						|
		local_bpa = local_bpm->action;
 | 
						|
		bgp_pbr_flush_entry(bgp, local_bpa,
 | 
						|
				    local_bpm, bpmer.bpme_found);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry)
 | 
						|
{
 | 
						|
	if (type_entry == FLOWSPEC_TCP_FLAGS)
 | 
						|
		return FLOWSPEC_DSCP;
 | 
						|
	if (type_entry == FLOWSPEC_DSCP)
 | 
						|
		return FLOWSPEC_FLOW_LABEL;
 | 
						|
	if (type_entry == FLOWSPEC_FLOW_LABEL)
 | 
						|
		return FLOWSPEC_PKT_LEN;
 | 
						|
	if (type_entry == FLOWSPEC_PKT_LEN)
 | 
						|
		return FLOWSPEC_FRAGMENT;
 | 
						|
	if (type_entry == FLOWSPEC_FRAGMENT)
 | 
						|
		return FLOWSPEC_ICMP_TYPE;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_icmp_action(struct bgp *bgp, struct bgp_path_info *path,
 | 
						|
				struct bgp_pbr_filter *bpf,
 | 
						|
				struct bgp_pbr_or_filter *bpof, bool add,
 | 
						|
				struct nexthop *nh, float *rate)
 | 
						|
{
 | 
						|
	struct bgp_pbr_range_port srcp, dstp;
 | 
						|
	struct bgp_pbr_val_mask *icmp_type, *icmp_code;
 | 
						|
	struct listnode *tnode, *cnode;
 | 
						|
 | 
						|
	if (!bpf)
 | 
						|
		return;
 | 
						|
	if (bpf->protocol != IPPROTO_ICMP)
 | 
						|
		return;
 | 
						|
	bpf->src_port = &srcp;
 | 
						|
	bpf->dst_port = &dstp;
 | 
						|
	/* parse icmp type and lookup appropriate icmp code
 | 
						|
	 * if no icmp code found, create as many entryes as
 | 
						|
	 * there are listed icmp codes for that icmp type
 | 
						|
	 */
 | 
						|
	if (!bpof->icmp_type) {
 | 
						|
		srcp.min_port = 0;
 | 
						|
		srcp.max_port = 255;
 | 
						|
		for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) {
 | 
						|
			dstp.min_port = icmp_code->val;
 | 
						|
			if (add)
 | 
						|
				bgp_pbr_policyroute_add_to_zebra_unit(
 | 
						|
					bgp, path, bpf, nh, rate);
 | 
						|
			else
 | 
						|
				bgp_pbr_policyroute_remove_from_zebra_unit(
 | 
						|
					bgp, path, bpf);
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	for (ALL_LIST_ELEMENTS_RO(bpof->icmp_type, tnode, icmp_type)) {
 | 
						|
		srcp.min_port = icmp_type->val;
 | 
						|
		srcp.max_port = 0;
 | 
						|
		dstp.max_port = 0;
 | 
						|
		/* only icmp type. create an entry only with icmp type */
 | 
						|
		if (!bpof->icmp_code) {
 | 
						|
			/* icmp type is not one of the above
 | 
						|
			 * forge an entry only based on the icmp type
 | 
						|
			 */
 | 
						|
			dstp.min_port = 0;
 | 
						|
			dstp.max_port = 255;
 | 
						|
			if (add)
 | 
						|
				bgp_pbr_policyroute_add_to_zebra_unit(
 | 
						|
					bgp, path, bpf, nh, rate);
 | 
						|
			else
 | 
						|
				bgp_pbr_policyroute_remove_from_zebra_unit(
 | 
						|
					bgp, path, bpf);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) {
 | 
						|
			dstp.min_port = icmp_code->val;
 | 
						|
			if (add)
 | 
						|
				bgp_pbr_policyroute_add_to_zebra_unit(
 | 
						|
					bgp, path, bpf, nh, rate);
 | 
						|
			else
 | 
						|
				bgp_pbr_policyroute_remove_from_zebra_unit(
 | 
						|
					bgp, path, bpf);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
	struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf,
 | 
						|
	struct bgp_pbr_or_filter *bpof, uint8_t type_entry)
 | 
						|
{
 | 
						|
	struct listnode *node, *nnode;
 | 
						|
	struct bgp_pbr_val_mask *valmask;
 | 
						|
	uint8_t next_type_entry;
 | 
						|
	struct list *orig_list;
 | 
						|
	struct bgp_pbr_val_mask **target_val;
 | 
						|
 | 
						|
	if (type_entry == 0) {
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	next_type_entry = bgp_pbr_next_type_entry(type_entry);
 | 
						|
	if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) {
 | 
						|
		orig_list = bpof->tcpflags;
 | 
						|
		target_val = &bpf->tcp_flags;
 | 
						|
	} else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
 | 
						|
		orig_list = bpof->dscp;
 | 
						|
		target_val = &bpf->dscp;
 | 
						|
	} else if (type_entry == FLOWSPEC_FLOW_LABEL && bpof->flowlabel) {
 | 
						|
		orig_list = bpof->flowlabel;
 | 
						|
		target_val = &bpf->flow_label;
 | 
						|
	} else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
 | 
						|
		orig_list = bpof->pkt_len;
 | 
						|
		target_val = &bpf->pkt_len_val;
 | 
						|
	} else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) {
 | 
						|
		orig_list = bpof->fragment;
 | 
						|
		target_val = &bpf->fragment;
 | 
						|
	} else if (type_entry == FLOWSPEC_ICMP_TYPE &&
 | 
						|
		   (bpof->icmp_type || bpof->icmp_code)) {
 | 
						|
		/* enumerate list for icmp - must be last one  */
 | 
						|
		bgp_pbr_icmp_action(bgp, path, bpf, bpof, false, NULL, NULL);
 | 
						|
		return;
 | 
						|
	} else {
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, next_type_entry);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) {
 | 
						|
		*target_val = valmask;
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, next_type_entry);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_policyroute_remove_from_zebra(
 | 
						|
	struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf,
 | 
						|
	struct bgp_pbr_or_filter *bpof)
 | 
						|
{
 | 
						|
	if (!bpof) {
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (bpof->tcpflags)
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, FLOWSPEC_TCP_FLAGS);
 | 
						|
	else if (bpof->dscp)
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, FLOWSPEC_DSCP);
 | 
						|
	else if (bpof->flowlabel)
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, FLOWSPEC_FLOW_LABEL);
 | 
						|
	else if (bpof->pkt_len)
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, FLOWSPEC_PKT_LEN);
 | 
						|
	else if (bpof->fragment)
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, FLOWSPEC_FRAGMENT);
 | 
						|
	else if (bpof->icmp_type || bpof->icmp_code)
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, FLOWSPEC_ICMP_TYPE);
 | 
						|
	else
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf);
 | 
						|
	/* flush bpof */
 | 
						|
	if (bpof->tcpflags)
 | 
						|
		list_delete_all_node(bpof->tcpflags);
 | 
						|
	if (bpof->dscp)
 | 
						|
		list_delete_all_node(bpof->dscp);
 | 
						|
	if (bpof->flowlabel)
 | 
						|
		list_delete_all_node(bpof->flowlabel);
 | 
						|
	if (bpof->pkt_len)
 | 
						|
		list_delete_all_node(bpof->pkt_len);
 | 
						|
	if (bpof->fragment)
 | 
						|
		list_delete_all_node(bpof->fragment);
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add)
 | 
						|
{
 | 
						|
	struct bgp_pbr_range_port *src_port;
 | 
						|
	struct bgp_pbr_range_port *dst_port;
 | 
						|
	struct bgp_pbr_range_port *pkt_len;
 | 
						|
	char bufsrc[64], bufdst[64];
 | 
						|
	char buffer[64];
 | 
						|
	int remaining_len = 0;
 | 
						|
	char protocol_str[16];
 | 
						|
 | 
						|
	if (!bpf)
 | 
						|
		return;
 | 
						|
	src_port = bpf->src_port;
 | 
						|
	dst_port = bpf->dst_port;
 | 
						|
	pkt_len = bpf->pkt_len;
 | 
						|
 | 
						|
	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, sizeof(buffer),
 | 
						|
					  "type %d, code %d",
 | 
						|
					  src_port->min_port,
 | 
						|
					  dst_port->min_port);
 | 
						|
	else if (bpf->protocol == IPPROTO_UDP ||
 | 
						|
		 bpf->protocol == IPPROTO_TCP) {
 | 
						|
 | 
						|
		if (src_port && src_port->min_port)
 | 
						|
			remaining_len += snprintf(buffer,
 | 
						|
						  sizeof(buffer),
 | 
						|
						  "from [%u:%u]",
 | 
						|
						  src_port->min_port,
 | 
						|
						  src_port->max_port ?
 | 
						|
						  src_port->max_port :
 | 
						|
						  src_port->min_port);
 | 
						|
		if (dst_port && dst_port->min_port)
 | 
						|
			remaining_len += snprintf(buffer +
 | 
						|
						  remaining_len,
 | 
						|
						  sizeof(buffer)
 | 
						|
						  - remaining_len,
 | 
						|
						  "to [%u:%u]",
 | 
						|
						  dst_port->min_port,
 | 
						|
						  dst_port->max_port ?
 | 
						|
						  dst_port->max_port :
 | 
						|
						  dst_port->min_port);
 | 
						|
	}
 | 
						|
	if (pkt_len && (pkt_len->min_port || pkt_len->max_port)) {
 | 
						|
		remaining_len += snprintf(buffer + remaining_len,
 | 
						|
					  sizeof(buffer)
 | 
						|
					  - remaining_len,
 | 
						|
					  " len [%u:%u]",
 | 
						|
					  pkt_len->min_port,
 | 
						|
					  pkt_len->max_port ?
 | 
						|
					  pkt_len->max_port :
 | 
						|
					  pkt_len->min_port);
 | 
						|
	} else if (bpf->pkt_len_val) {
 | 
						|
		remaining_len += snprintf(buffer + remaining_len,
 | 
						|
					  sizeof(buffer)
 | 
						|
					  - remaining_len,
 | 
						|
					  " %s len %u",
 | 
						|
					  bpf->pkt_len_val->mask
 | 
						|
					  ? "!" : "",
 | 
						|
					  bpf->pkt_len_val->val);
 | 
						|
	}
 | 
						|
	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);
 | 
						|
	}
 | 
						|
	if (bpf->dscp) {
 | 
						|
		snprintf(buffer + remaining_len,
 | 
						|
			 sizeof(buffer)
 | 
						|
			 - remaining_len,
 | 
						|
			 "%s dscp %d",
 | 
						|
			 bpf->dscp->mask
 | 
						|
			 ? "!" : "",
 | 
						|
			 bpf->dscp->val);
 | 
						|
	}
 | 
						|
	if (bpf->flow_label) {
 | 
						|
		snprintf(buffer + remaining_len,
 | 
						|
			 sizeof(buffer)
 | 
						|
			 - remaining_len,
 | 
						|
			 "%s flow_label %d",
 | 
						|
			 bpf->flow_label->mask
 | 
						|
			 ? "!" : "",
 | 
						|
			 bpf->flow_label->val);
 | 
						|
	}
 | 
						|
	zlog_debug("BGP: %s FS PBR from %s to %s, %s %s",
 | 
						|
		  add ? "adding" : "removing",
 | 
						|
		  bpf->src == NULL ? "<all>" :
 | 
						|
		  prefix2str(bpf->src, bufsrc, sizeof(bufsrc)),
 | 
						|
		  bpf->dst == NULL ? "<all>" :
 | 
						|
		  prefix2str(bpf->dst, bufdst, sizeof(bufdst)),
 | 
						|
		  protocol_str, buffer);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
 | 
						|
						  struct bgp_path_info *path,
 | 
						|
						  struct bgp_pbr_filter *bpf,
 | 
						|
						  struct nexthop *nh,
 | 
						|
						  float *rate)
 | 
						|
{
 | 
						|
	struct bgp_pbr_match temp;
 | 
						|
	struct bgp_pbr_match_entry temp2;
 | 
						|
	struct bgp_pbr_match *bpm;
 | 
						|
	struct bgp_pbr_match_entry *bpme = NULL;
 | 
						|
	struct bgp_pbr_action temp3;
 | 
						|
	struct bgp_pbr_action *bpa = NULL;
 | 
						|
	struct bgp_pbr_match_entry_remain bpmer;
 | 
						|
	struct bgp_pbr_rule_remain bprr;
 | 
						|
	struct bgp_pbr_range_port *src_port;
 | 
						|
	struct bgp_pbr_range_port *dst_port;
 | 
						|
	struct bgp_pbr_range_port *pkt_len;
 | 
						|
	struct bgp_pbr_rule pbr_rule;
 | 
						|
	struct bgp_pbr_rule *bpr;
 | 
						|
	bool bpr_found = false;
 | 
						|
	bool bpme_found = false;
 | 
						|
 | 
						|
	if (!bpf)
 | 
						|
		return;
 | 
						|
	src_port = bpf->src_port;
 | 
						|
	dst_port = bpf->dst_port;
 | 
						|
	pkt_len = bpf->pkt_len;
 | 
						|
 | 
						|
	if (BGP_DEBUG(zebra, ZEBRA))
 | 
						|
		bgp_pbr_dump_entry(bpf, true);
 | 
						|
 | 
						|
	/* look for bpa first */
 | 
						|
	memset(&temp3, 0, sizeof(temp3));
 | 
						|
	if (rate)
 | 
						|
		temp3.rate = *rate;
 | 
						|
	if (nh)
 | 
						|
		memcpy(&temp3.nh, nh, sizeof(struct nexthop));
 | 
						|
	temp3.vrf_id = bpf->vrf_id;
 | 
						|
	temp3.afi = family2afi(bpf->family);
 | 
						|
	bpa = hash_get(bgp->pbr_action_hash, &temp3,
 | 
						|
		       bgp_pbr_action_alloc_intern);
 | 
						|
 | 
						|
	if (bpa->fwmark == 0) {
 | 
						|
		/* drop is handled by iptable */
 | 
						|
		if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
 | 
						|
			bpa->table_id = 0;
 | 
						|
			bpa->installed = true;
 | 
						|
		} else {
 | 
						|
			bpa->fwmark = bgp_zebra_tm_get_id();
 | 
						|
			bpa->table_id = bpa->fwmark;
 | 
						|
			bpa->installed = false;
 | 
						|
		}
 | 
						|
		bpa->bgp = bgp;
 | 
						|
		bpa->unique = ++bgp_pbr_action_counter_unique;
 | 
						|
		/* 0 value is forbidden */
 | 
						|
		bpa->install_in_progress = false;
 | 
						|
	}
 | 
						|
	if (bpf->type == BGP_PBR_IPRULE) {
 | 
						|
		memset(&pbr_rule, 0, sizeof(pbr_rule));
 | 
						|
		pbr_rule.vrf_id = bpf->vrf_id;
 | 
						|
		pbr_rule.priority = 20;
 | 
						|
		if (bpf->src) {
 | 
						|
			pbr_rule.flags |= MATCH_IP_SRC_SET;
 | 
						|
			prefix_copy(&pbr_rule.src, bpf->src);
 | 
						|
		}
 | 
						|
		if (bpf->dst) {
 | 
						|
			pbr_rule.flags |= MATCH_IP_DST_SET;
 | 
						|
			prefix_copy(&pbr_rule.dst, bpf->dst);
 | 
						|
		}
 | 
						|
		pbr_rule.action = bpa;
 | 
						|
		bpr = hash_get(bgp->pbr_rule_hash, &pbr_rule,
 | 
						|
			       bgp_pbr_rule_alloc_intern);
 | 
						|
		if (bpr && bpr->unique == 0) {
 | 
						|
			bpr->unique = ++bgp_pbr_action_counter_unique;
 | 
						|
			bpr->installed = false;
 | 
						|
			bpr->install_in_progress = false;
 | 
						|
			/* link bgp info to bpr */
 | 
						|
			bpr->path = (void *)path;
 | 
						|
		} else
 | 
						|
			bpr_found = true;
 | 
						|
		/* already installed */
 | 
						|
		if (bpr_found && bpr) {
 | 
						|
			struct bgp_path_info_extra *extra =
 | 
						|
				bgp_path_info_extra_get(path);
 | 
						|
 | 
						|
			if (extra &&
 | 
						|
			    listnode_lookup_nocheck(extra->bgp_fs_iprule,
 | 
						|
						    bpr)) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR_ERROR))
 | 
						|
					zlog_err("%s: entry %p/%p already installed in bgp pbr iprule",
 | 
						|
						 __func__, path, bpr);
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (!bpa->installed && !bpa->install_in_progress) {
 | 
						|
			bgp_send_pbr_rule_action(bpa, NULL, true);
 | 
						|
			bgp_zebra_announce_default(bgp, nh,
 | 
						|
						   bpa->afi,
 | 
						|
						   bpa->table_id, true);
 | 
						|
		}
 | 
						|
		/* ip rule add */
 | 
						|
		if (bpr && !bpr->installed)
 | 
						|
			bgp_send_pbr_rule_action(bpa, bpr, true);
 | 
						|
 | 
						|
		/* A previous entry may already exist
 | 
						|
		 * flush previous entry if necessary
 | 
						|
		 */
 | 
						|
		bprr.bpr_to_match = bpr;
 | 
						|
		bprr.bpr_found = NULL;
 | 
						|
		hash_walk(bgp->pbr_rule_hash, bgp_pbr_get_same_rule, &bprr);
 | 
						|
		if (bprr.bpr_found) {
 | 
						|
			static struct bgp_pbr_rule *local_bpr;
 | 
						|
			static struct bgp_pbr_action *local_bpa;
 | 
						|
 | 
						|
			local_bpr = bprr.bpr_found;
 | 
						|
			local_bpa = local_bpr->action;
 | 
						|
			bgp_pbr_flush_iprule(bgp, local_bpa,
 | 
						|
					     local_bpr);
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	/* then look for bpm */
 | 
						|
	memset(&temp, 0, sizeof(temp));
 | 
						|
	temp.vrf_id = bpf->vrf_id;
 | 
						|
	temp.family = bpf->family;
 | 
						|
	if (bpf->src)
 | 
						|
		temp.flags |= MATCH_IP_SRC_SET;
 | 
						|
	if (bpf->dst)
 | 
						|
		temp.flags |= MATCH_IP_DST_SET;
 | 
						|
 | 
						|
	if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
 | 
						|
		if (bpf->protocol == IPPROTO_ICMP)
 | 
						|
			temp.flags |= MATCH_ICMP_SET;
 | 
						|
		temp.flags |= MATCH_PORT_SRC_SET;
 | 
						|
	}
 | 
						|
	if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
 | 
						|
		if (bpf->protocol == IPPROTO_ICMP)
 | 
						|
			temp.flags |= MATCH_ICMP_SET;
 | 
						|
		temp.flags |= MATCH_PORT_DST_SET;
 | 
						|
	}
 | 
						|
	if (src_port && src_port->max_port)
 | 
						|
		temp.flags |= MATCH_PORT_SRC_RANGE_SET;
 | 
						|
	if (dst_port && dst_port->max_port)
 | 
						|
		temp.flags |= MATCH_PORT_DST_RANGE_SET;
 | 
						|
 | 
						|
	if (bpf->src == NULL || bpf->dst == NULL) {
 | 
						|
		if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
 | 
						|
			temp.type = IPSET_NET_PORT;
 | 
						|
		else
 | 
						|
			temp.type = IPSET_NET;
 | 
						|
	} else {
 | 
						|
		if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
 | 
						|
			temp.type = IPSET_NET_PORT_NET;
 | 
						|
		else
 | 
						|
			temp.type = IPSET_NET_NET;
 | 
						|
	}
 | 
						|
	if (pkt_len) {
 | 
						|
		temp.pkt_len_min = pkt_len->min_port;
 | 
						|
		if (pkt_len->max_port)
 | 
						|
			temp.pkt_len_max = pkt_len->max_port;
 | 
						|
	} else if (bpf->pkt_len_val) {
 | 
						|
		if (bpf->pkt_len_val->mask)
 | 
						|
			temp.flags |= MATCH_PKT_LEN_INVERSE_SET;
 | 
						|
		temp.pkt_len_min = bpf->pkt_len_val->val;
 | 
						|
	}
 | 
						|
	if (bpf->tcp_flags) {
 | 
						|
		temp.tcp_flags = bpf->tcp_flags->val;
 | 
						|
		temp.tcp_mask_flags = bpf->tcp_flags->mask;
 | 
						|
	}
 | 
						|
	if (bpf->dscp) {
 | 
						|
		if (bpf->dscp->mask)
 | 
						|
			temp.flags |= MATCH_DSCP_INVERSE_SET;
 | 
						|
		else
 | 
						|
			temp.flags |= MATCH_DSCP_SET;
 | 
						|
		temp.dscp_value = bpf->dscp->val;
 | 
						|
	}
 | 
						|
	if (bpf->flow_label) {
 | 
						|
		if (bpf->flow_label->mask)
 | 
						|
			temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET;
 | 
						|
		else
 | 
						|
			temp.flags |= MATCH_FLOW_LABEL_SET;
 | 
						|
		temp.flow_label = bpf->flow_label->val;
 | 
						|
	}
 | 
						|
	if (bpf->fragment) {
 | 
						|
		if (bpf->fragment->mask)
 | 
						|
			temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
 | 
						|
		temp.fragment = bpf->fragment->val;
 | 
						|
	}
 | 
						|
	if (bpf->protocol) {
 | 
						|
		temp.protocol = bpf->protocol;
 | 
						|
		temp.flags |= MATCH_PROTOCOL_SET;
 | 
						|
	}
 | 
						|
	temp.action = bpa;
 | 
						|
	bpm = hash_get(bgp->pbr_match_hash, &temp,
 | 
						|
		       bgp_pbr_match_alloc_intern);
 | 
						|
 | 
						|
	/* new, then self allocate ipset_name and unique */
 | 
						|
	if (bpm->unique == 0) {
 | 
						|
		bpm->unique = ++bgp_pbr_match_counter_unique;
 | 
						|
		/* 0 value is forbidden */
 | 
						|
		snprintf(bpm->ipset_name, sizeof(bpm->ipset_name),
 | 
						|
			 "match%p", bpm);
 | 
						|
		bpm->entry_hash = hash_create_size(8,
 | 
						|
				   bgp_pbr_match_entry_hash_key,
 | 
						|
				   bgp_pbr_match_entry_hash_equal,
 | 
						|
				   "Match Entry Hash");
 | 
						|
		bpm->installed = false;
 | 
						|
 | 
						|
		/* unique2 should be updated too */
 | 
						|
		bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
 | 
						|
		bpm->installed_in_iptable = false;
 | 
						|
		bpm->install_in_progress = false;
 | 
						|
		bpm->install_iptable_in_progress = false;
 | 
						|
	}
 | 
						|
 | 
						|
	memset(&temp2, 0, sizeof(temp2));
 | 
						|
	if (bpf->src)
 | 
						|
		prefix_copy(&temp2.src, bpf->src);
 | 
						|
	else
 | 
						|
		temp2.src.family = bpf->family;
 | 
						|
	if (bpf->dst)
 | 
						|
		prefix_copy(&temp2.dst, bpf->dst);
 | 
						|
	else
 | 
						|
		temp2.dst.family = bpf->family;
 | 
						|
	temp2.src_port_min = src_port ? src_port->min_port : 0;
 | 
						|
	temp2.dst_port_min = dst_port ? dst_port->min_port : 0;
 | 
						|
	temp2.src_port_max = src_port ? src_port->max_port : 0;
 | 
						|
	temp2.dst_port_max = dst_port ? dst_port->max_port : 0;
 | 
						|
	temp2.proto = bpf->protocol;
 | 
						|
	bpme = hash_get(bpm->entry_hash, &temp2,
 | 
						|
			bgp_pbr_match_entry_alloc_intern);
 | 
						|
	if (bpme->unique == 0) {
 | 
						|
		bpme->unique = ++bgp_pbr_match_entry_counter_unique;
 | 
						|
		/* 0 value is forbidden */
 | 
						|
		bpme->backpointer = bpm;
 | 
						|
		bpme->installed = false;
 | 
						|
		bpme->install_in_progress = false;
 | 
						|
		/* link bgp info to bpme */
 | 
						|
		bpme->path = (void *)path;
 | 
						|
	} else
 | 
						|
		bpme_found = true;
 | 
						|
 | 
						|
	/* already installed */
 | 
						|
	if (bpme_found) {
 | 
						|
		struct bgp_path_info_extra *extra =
 | 
						|
			bgp_path_info_extra_get(path);
 | 
						|
 | 
						|
		if (extra &&
 | 
						|
		    listnode_lookup_nocheck(extra->bgp_fs_pbr, bpme)) {
 | 
						|
			if (BGP_DEBUG(pbr, PBR_ERROR))
 | 
						|
				zlog_err(
 | 
						|
					"%s: entry %p/%p already installed in bgp pbr",
 | 
						|
					__func__, path, bpme);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* BGP FS: append entry to zebra
 | 
						|
	 * - policies are not routing entries and as such
 | 
						|
	 * route replace semantics don't necessarily follow
 | 
						|
	 * through to policy entries
 | 
						|
	 * - because of that, not all policing information will be stored
 | 
						|
	 * into zebra. and non selected policies will be suppressed from zebra
 | 
						|
	 * - as consequence, in order to bring consistency
 | 
						|
	 * a policy will be added, then ifan ecmp policy exists,
 | 
						|
	 * it will be suppressed subsequently
 | 
						|
	 */
 | 
						|
	/* ip rule add */
 | 
						|
	if (!bpa->installed && !bpa->install_in_progress) {
 | 
						|
		bgp_send_pbr_rule_action(bpa, NULL, true);
 | 
						|
		bgp_zebra_announce_default(bgp, nh,
 | 
						|
					   bpa->afi, bpa->table_id, true);
 | 
						|
	}
 | 
						|
 | 
						|
	/* ipset create */
 | 
						|
	if (!bpm->installed)
 | 
						|
		bgp_send_pbr_ipset_match(bpm, true);
 | 
						|
	/* ipset add */
 | 
						|
	if (!bpme->installed)
 | 
						|
		bgp_send_pbr_ipset_entry_match(bpme, true);
 | 
						|
 | 
						|
	/* iptables */
 | 
						|
	if (!bpm->installed_in_iptable)
 | 
						|
		bgp_send_pbr_iptable(bpa, bpm, true);
 | 
						|
 | 
						|
	/* A previous entry may already exist
 | 
						|
	 * flush previous entry if necessary
 | 
						|
	 */
 | 
						|
	bpmer.bpme_to_match = bpme;
 | 
						|
	bpmer.bpme_found = NULL;
 | 
						|
	hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
 | 
						|
	if (bpmer.bpme_found) {
 | 
						|
		static struct bgp_pbr_match *local_bpm;
 | 
						|
		static struct bgp_pbr_action *local_bpa;
 | 
						|
 | 
						|
		local_bpm = bpmer.bpme_found->backpointer;
 | 
						|
		local_bpa = local_bpm->action;
 | 
						|
		bgp_pbr_flush_entry(bgp, local_bpa,
 | 
						|
				    local_bpm, bpmer.bpme_found);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
	struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf,
 | 
						|
	struct bgp_pbr_or_filter *bpof, struct nexthop *nh, float *rate,
 | 
						|
	uint8_t type_entry)
 | 
						|
{
 | 
						|
	struct listnode *node, *nnode;
 | 
						|
	struct bgp_pbr_val_mask *valmask;
 | 
						|
	uint8_t next_type_entry;
 | 
						|
	struct list *orig_list;
 | 
						|
	struct bgp_pbr_val_mask **target_val;
 | 
						|
 | 
						|
	if (type_entry == 0) {
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	next_type_entry = bgp_pbr_next_type_entry(type_entry);
 | 
						|
	if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) {
 | 
						|
		orig_list = bpof->tcpflags;
 | 
						|
		target_val = &bpf->tcp_flags;
 | 
						|
	} else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
 | 
						|
		orig_list = bpof->dscp;
 | 
						|
		target_val = &bpf->dscp;
 | 
						|
	} else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
 | 
						|
		orig_list = bpof->pkt_len;
 | 
						|
		target_val = &bpf->pkt_len_val;
 | 
						|
	} else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) {
 | 
						|
		orig_list = bpof->fragment;
 | 
						|
		target_val = &bpf->fragment;
 | 
						|
	} else if (type_entry == FLOWSPEC_ICMP_TYPE &&
 | 
						|
		   (bpof->icmp_type || bpof->icmp_code)) {
 | 
						|
		/* enumerate list for icmp - must be last one  */
 | 
						|
		bgp_pbr_icmp_action(bgp, path, bpf, bpof, true, nh, rate);
 | 
						|
		return;
 | 
						|
	} else {
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, nh, rate, next_type_entry);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) {
 | 
						|
		*target_val = valmask;
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, nh, rate, next_type_entry);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
 | 
						|
					     struct bgp_path_info *path,
 | 
						|
					     struct bgp_pbr_filter *bpf,
 | 
						|
					     struct bgp_pbr_or_filter *bpof,
 | 
						|
					     struct nexthop *nh, float *rate)
 | 
						|
{
 | 
						|
	if (!bpof) {
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (bpof->tcpflags)
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, nh, rate, FLOWSPEC_TCP_FLAGS);
 | 
						|
	else if (bpof->dscp)
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, nh, rate, FLOWSPEC_DSCP);
 | 
						|
	else if (bpof->pkt_len)
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, nh, rate, FLOWSPEC_PKT_LEN);
 | 
						|
	else if (bpof->fragment)
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, nh, rate, FLOWSPEC_FRAGMENT);
 | 
						|
	else if (bpof->icmp_type || bpof->icmp_code)
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_recursive(
 | 
						|
			bgp, path, bpf, bpof, nh, rate, FLOWSPEC_ICMP_TYPE);
 | 
						|
	else
 | 
						|
		bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate);
 | 
						|
	/* flush bpof */
 | 
						|
	if (bpof->tcpflags)
 | 
						|
		list_delete_all_node(bpof->tcpflags);
 | 
						|
	if (bpof->dscp)
 | 
						|
		list_delete_all_node(bpof->dscp);
 | 
						|
	if (bpof->pkt_len)
 | 
						|
		list_delete_all_node(bpof->pkt_len);
 | 
						|
	if (bpof->fragment)
 | 
						|
		list_delete_all_node(bpof->fragment);
 | 
						|
	if (bpof->icmp_type)
 | 
						|
		list_delete_all_node(bpof->icmp_type);
 | 
						|
	if (bpof->icmp_code)
 | 
						|
		list_delete_all_node(bpof->icmp_code);
 | 
						|
}
 | 
						|
 | 
						|
static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
 | 
						|
				 struct bgp_pbr_entry_main *api, bool add)
 | 
						|
{
 | 
						|
	struct nexthop nh;
 | 
						|
	int i = 0;
 | 
						|
	int continue_loop = 1;
 | 
						|
	float rate = 0;
 | 
						|
	struct prefix *src = NULL, *dst = NULL;
 | 
						|
	uint8_t proto = 0;
 | 
						|
	struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL;
 | 
						|
	struct bgp_pbr_range_port range, range_icmp_code;
 | 
						|
	struct bgp_pbr_range_port pkt_len;
 | 
						|
	struct bgp_pbr_filter bpf;
 | 
						|
	uint8_t kind_enum;
 | 
						|
	struct bgp_pbr_or_filter bpof;
 | 
						|
	struct bgp_pbr_val_mask bpvm;
 | 
						|
 | 
						|
	memset(&nh, 0, sizeof(struct nexthop));
 | 
						|
	memset(&bpf, 0, sizeof(struct bgp_pbr_filter));
 | 
						|
	memset(&bpof, 0, sizeof(struct bgp_pbr_or_filter));
 | 
						|
	if (api->match_bitmask & PREFIX_SRC_PRESENT ||
 | 
						|
	    (api->type == BGP_PBR_IPRULE &&
 | 
						|
	     api->match_bitmask_iprule & PREFIX_SRC_PRESENT))
 | 
						|
		src = &api->src_prefix;
 | 
						|
	if (api->match_bitmask & PREFIX_DST_PRESENT ||
 | 
						|
	    (api->type == BGP_PBR_IPRULE &&
 | 
						|
	     api->match_bitmask_iprule & PREFIX_DST_PRESENT))
 | 
						|
		dst = &api->dst_prefix;
 | 
						|
	if (api->type == BGP_PBR_IPRULE)
 | 
						|
		bpf.type = api->type;
 | 
						|
	memset(&nh, 0, sizeof(struct nexthop));
 | 
						|
	nh.vrf_id = VRF_UNKNOWN;
 | 
						|
	if (api->match_protocol_num) {
 | 
						|
		proto = (uint8_t)api->protocol[0].value;
 | 
						|
		if (api->afi == AF_INET6 && proto == IPPROTO_ICMPV6)
 | 
						|
			proto = IPPROTO_ICMP;
 | 
						|
	}
 | 
						|
	/* if match_port is selected, then either src or dst port will be parsed
 | 
						|
	 * but not both at the same time
 | 
						|
	 */
 | 
						|
	if (api->match_port_num >= 1) {
 | 
						|
		bgp_pbr_extract(api->port,
 | 
						|
				api->match_port_num,
 | 
						|
				&range);
 | 
						|
		srcp = dstp = ⦥
 | 
						|
	} else if (api->match_src_port_num >= 1) {
 | 
						|
		bgp_pbr_extract(api->src_port,
 | 
						|
				api->match_src_port_num,
 | 
						|
				&range);
 | 
						|
		srcp = ⦥
 | 
						|
		dstp = NULL;
 | 
						|
	} else if (api->match_dst_port_num >= 1) {
 | 
						|
		bgp_pbr_extract(api->dst_port,
 | 
						|
				api->match_dst_port_num,
 | 
						|
				&range);
 | 
						|
		dstp = ⦥
 | 
						|
		srcp = NULL;
 | 
						|
	}
 | 
						|
	if (api->match_icmp_type_num >= 1) {
 | 
						|
		proto = IPPROTO_ICMP;
 | 
						|
		if (bgp_pbr_extract(api->icmp_type,
 | 
						|
				    api->match_icmp_type_num,
 | 
						|
				    &range))
 | 
						|
			srcp = ⦥
 | 
						|
		else {
 | 
						|
			bpof.icmp_type = list_new();
 | 
						|
			bgp_pbr_extract_enumerate(api->icmp_type,
 | 
						|
						  api->match_icmp_type_num,
 | 
						|
						  OPERATOR_UNARY_OR,
 | 
						|
						  bpof.icmp_type,
 | 
						|
						  FLOWSPEC_ICMP_TYPE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (api->match_icmp_code_num >= 1) {
 | 
						|
		proto = IPPROTO_ICMP;
 | 
						|
		if (bgp_pbr_extract(api->icmp_code,
 | 
						|
				    api->match_icmp_code_num,
 | 
						|
				    &range_icmp_code))
 | 
						|
			dstp = &range_icmp_code;
 | 
						|
		else {
 | 
						|
			bpof.icmp_code = list_new();
 | 
						|
			bgp_pbr_extract_enumerate(api->icmp_code,
 | 
						|
						  api->match_icmp_code_num,
 | 
						|
						  OPERATOR_UNARY_OR,
 | 
						|
						  bpof.icmp_code,
 | 
						|
						  FLOWSPEC_ICMP_CODE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (api->match_tcpflags_num) {
 | 
						|
		kind_enum = bgp_pbr_match_val_get_operator(api->tcpflags,
 | 
						|
						   api->match_tcpflags_num);
 | 
						|
		if (kind_enum == OPERATOR_UNARY_AND) {
 | 
						|
			bpf.tcp_flags = &bpvm;
 | 
						|
			bgp_pbr_extract_enumerate(api->tcpflags,
 | 
						|
						  api->match_tcpflags_num,
 | 
						|
						  OPERATOR_UNARY_AND,
 | 
						|
						  bpf.tcp_flags,
 | 
						|
						  FLOWSPEC_TCP_FLAGS);
 | 
						|
		} else if (kind_enum == OPERATOR_UNARY_OR) {
 | 
						|
			bpof.tcpflags = list_new();
 | 
						|
			bgp_pbr_extract_enumerate(api->tcpflags,
 | 
						|
						  api->match_tcpflags_num,
 | 
						|
						  OPERATOR_UNARY_OR,
 | 
						|
						  bpof.tcpflags,
 | 
						|
						  FLOWSPEC_TCP_FLAGS);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (api->match_packet_length_num) {
 | 
						|
		bool ret;
 | 
						|
 | 
						|
		ret = bgp_pbr_extract(api->packet_length,
 | 
						|
				      api->match_packet_length_num,
 | 
						|
				      &pkt_len);
 | 
						|
		if (ret)
 | 
						|
			bpf.pkt_len = &pkt_len;
 | 
						|
		else {
 | 
						|
			bpof.pkt_len = list_new();
 | 
						|
			bgp_pbr_extract_enumerate(api->packet_length,
 | 
						|
						  api->match_packet_length_num,
 | 
						|
						  OPERATOR_UNARY_OR,
 | 
						|
						  bpof.pkt_len,
 | 
						|
						  FLOWSPEC_PKT_LEN);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (api->match_dscp_num >= 1) {
 | 
						|
		bpof.dscp = list_new();
 | 
						|
		bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num,
 | 
						|
					  OPERATOR_UNARY_OR,
 | 
						|
					  bpof.dscp, FLOWSPEC_DSCP);
 | 
						|
	}
 | 
						|
	if (api->match_fragment_num) {
 | 
						|
		bpof.fragment = list_new();
 | 
						|
		bgp_pbr_extract_enumerate(api->fragment,
 | 
						|
					  api->match_fragment_num,
 | 
						|
					  OPERATOR_UNARY_OR,
 | 
						|
					  bpof.fragment,
 | 
						|
					  FLOWSPEC_FRAGMENT);
 | 
						|
	}
 | 
						|
	bpf.vrf_id = api->vrf_id;
 | 
						|
	bpf.src = src;
 | 
						|
	bpf.dst = dst;
 | 
						|
	bpf.protocol = proto;
 | 
						|
	bpf.src_port = srcp;
 | 
						|
	bpf.dst_port = dstp;
 | 
						|
	bpf.family = afi2family(api->afi);
 | 
						|
	if (!add) {
 | 
						|
		bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	/* no action for add = true */
 | 
						|
	for (i = 0; i < api->action_num; i++) {
 | 
						|
		switch (api->actions[i].action) {
 | 
						|
		case ACTION_TRAFFICRATE:
 | 
						|
			/* drop packet */
 | 
						|
			if (api->actions[i].u.r.rate == 0) {
 | 
						|
				nh.vrf_id = api->vrf_id;
 | 
						|
				nh.type = NEXTHOP_TYPE_BLACKHOLE;
 | 
						|
				bgp_pbr_policyroute_add_to_zebra(
 | 
						|
					bgp, path, &bpf, &bpof, &nh, &rate);
 | 
						|
			} else {
 | 
						|
				/* update rate. can be reentrant */
 | 
						|
				rate = api->actions[i].u.r.rate;
 | 
						|
				if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
					bgp_pbr_print_policy_route(api);
 | 
						|
					zlog_warn("PBR: ignoring Set action rate %f",
 | 
						|
						  api->actions[i].u.r.rate);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case ACTION_TRAFFIC_ACTION:
 | 
						|
			if (api->actions[i].u.za.filter
 | 
						|
			    & TRAFFIC_ACTION_SAMPLE) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
					bgp_pbr_print_policy_route(api);
 | 
						|
					zlog_warn("PBR: Sample action Ignored");
 | 
						|
				}
 | 
						|
			}
 | 
						|
#if 0
 | 
						|
			if (api->actions[i].u.za.filter
 | 
						|
			    & TRAFFIC_ACTION_DISTRIBUTE) {
 | 
						|
				if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
					bgp_pbr_print_policy_route(api);
 | 
						|
					zlog_warn("PBR: Distribute action Applies");
 | 
						|
				}
 | 
						|
				continue_loop = 0;
 | 
						|
				/* continue forwarding entry as before
 | 
						|
				 * no action
 | 
						|
				 */
 | 
						|
			}
 | 
						|
#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
 | 
						|
			/* terminate action: run other filters
 | 
						|
			 */
 | 
						|
			break;
 | 
						|
		case ACTION_REDIRECT_IP:
 | 
						|
			nh.vrf_id = api->vrf_id;
 | 
						|
			if (api->afi == AFI_IP) {
 | 
						|
				nh.type = NEXTHOP_TYPE_IPV4;
 | 
						|
				nh.gate.ipv4.s_addr =
 | 
						|
					api->actions[i].u.zr.
 | 
						|
					redirect_ip_v4.s_addr;
 | 
						|
			} else {
 | 
						|
				nh.type = NEXTHOP_TYPE_IPV6;
 | 
						|
				memcpy(&nh.gate.ipv6,
 | 
						|
				       &api->actions[i].u.zr.redirect_ip_v6,
 | 
						|
				       sizeof(struct in6_addr));
 | 
						|
			}
 | 
						|
			bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
 | 
						|
							 &nh, &rate);
 | 
						|
			/* XXX combination with REDIRECT_VRF
 | 
						|
			 * + REDIRECT_NH_IP not done
 | 
						|
			 */
 | 
						|
			continue_loop = 0;
 | 
						|
			break;
 | 
						|
		case ACTION_REDIRECT:
 | 
						|
			if (api->afi == AFI_IP)
 | 
						|
				nh.type = NEXTHOP_TYPE_IPV4;
 | 
						|
			else
 | 
						|
				nh.type = NEXTHOP_TYPE_IPV6;
 | 
						|
			nh.vrf_id = api->actions[i].u.redirect_vrf;
 | 
						|
			bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
 | 
						|
							 &nh, &rate);
 | 
						|
			continue_loop = 0;
 | 
						|
			break;
 | 
						|
		case ACTION_MARKING:
 | 
						|
			if (BGP_DEBUG(pbr, PBR)) {
 | 
						|
				bgp_pbr_print_policy_route(api);
 | 
						|
				zlog_warn("PBR: Set DSCP/FlowLabel %u Ignored",
 | 
						|
					  api->actions[i].u.marking_dscp);
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (continue_loop == 0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p,
 | 
						|
			  struct bgp_path_info *info, afi_t afi, safi_t safi,
 | 
						|
			  bool nlri_update)
 | 
						|
{
 | 
						|
	struct bgp_pbr_entry_main api;
 | 
						|
 | 
						|
	if (safi != SAFI_FLOWSPEC)
 | 
						|
		return; /* not supported */
 | 
						|
	/* Make Zebra API structure. */
 | 
						|
	memset(&api, 0, sizeof(api));
 | 
						|
	api.vrf_id = bgp->vrf_id;
 | 
						|
	api.afi = afi;
 | 
						|
 | 
						|
	if (!bgp_zebra_tm_chunk_obtained()) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR_ERROR))
 | 
						|
			flog_err(EC_BGP_TABLE_CHUNK,
 | 
						|
				 "%s: table chunk not obtained yet", __func__);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
 | 
						|
		if (BGP_DEBUG(pbr, PBR_ERROR))
 | 
						|
			flog_err(EC_BGP_FLOWSPEC_INSTALLATION,
 | 
						|
				 "%s: cancel updating entry %p in bgp pbr",
 | 
						|
				 __func__, info);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
 | 
						|
}
 | 
						|
 | 
						|
int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a,
 | 
						|
			  const struct bgp_pbr_interface *b)
 | 
						|
{
 | 
						|
	return strcmp(a->name, b->name);
 | 
						|
}
 | 
						|
 | 
						|
struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name,
 | 
						|
					   struct bgp_pbr_interface_head *head)
 | 
						|
{
 | 
						|
	struct bgp_pbr_interface pbr_if;
 | 
						|
 | 
						|
	strlcpy(pbr_if.name, name, sizeof(pbr_if.name));
 | 
						|
	return (RB_FIND(bgp_pbr_interface_head,
 | 
						|
			head, &pbr_if));
 | 
						|
}
 | 
						|
 | 
						|
/* this function resets to the default policy routing
 | 
						|
 * go back to default status
 | 
						|
 */
 | 
						|
void bgp_pbr_reset(struct bgp *bgp, afi_t afi)
 | 
						|
{
 | 
						|
	struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
 | 
						|
	struct bgp_pbr_interface_head *head;
 | 
						|
	struct bgp_pbr_interface *pbr_if;
 | 
						|
 | 
						|
	if (!bgp_pbr_cfg)
 | 
						|
		return;
 | 
						|
	if (afi == AFI_IP)
 | 
						|
		head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
 | 
						|
	else
 | 
						|
		head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
 | 
						|
	while (!RB_EMPTY(bgp_pbr_interface_head, head)) {
 | 
						|
		pbr_if = RB_ROOT(bgp_pbr_interface_head, head);
 | 
						|
		RB_REMOVE(bgp_pbr_interface_head, head, pbr_if);
 | 
						|
		XFREE(MTYPE_TMP, pbr_if);
 | 
						|
	}
 | 
						|
}
 |