Implement MSDP peer incoming/outgoing SA filter.
Note
----
Cisco extended access list has a special meaning: the first address is
the source address to filter.
Example:
! The rules below filter some LAN prefix to be leaked out
access-list filter-lan-source deny ip 192.168.0.0 0.0.255.255 224.0.0.0 0.255.255.255
access-list filter-lan-source permit any
router pim
msdp peer 192.168.0.1 sa-filter filter-lan-source out
! The rules below filter some special management group from being
! learned
access-list filter-management-group deny 230.0.0.0 0.255.255.255
access-list filter-management-group permit any
router pim
msdp peer 192.168.0.1 sa-filter filter-management-group in
Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
return ret;
}
+DEFPY(msdp_peer_sa_filter, msdp_peer_sa_filter_cmd,
+ "msdp peer A.B.C.D$peer sa-filter ACL_NAME$acl_name <in|out>$dir",
+ CFG_MSDP_STR
+ "Configure MSDP peer\n"
+ "MSDP Peer address\n"
+ "SA access-list filter\n"
+ "SA access-list name\n"
+ "Filter incoming SAs\n"
+ "Filter outgoing SAs\n")
+{
+ const struct lyd_node *peer_node;
+ char xpath[XPATH_MAXLEN + 24];
+
+ snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']",
+ VTY_CURR_XPATH, peer_str);
+ peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath);
+ if (peer_node == NULL) {
+ vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str);
+ return CMD_SUCCESS;
+ }
+
+ if (strcmp(dir, "in") == 0)
+ nb_cli_enqueue_change(vty, "./sa-filter-in", NB_OP_MODIFY,
+ acl_name);
+ else
+ nb_cli_enqueue_change(vty, "./sa-filter-out", NB_OP_MODIFY,
+ acl_name);
+
+ return nb_cli_apply_changes(vty, "%s", xpath);
+}
+
+DEFPY(no_msdp_peer_sa_filter, no_ip_msdp_peer_sa_filter_cmd,
+ "no msdp peer A.B.C.D$peer sa-filter ACL_NAME <in|out>$dir",
+ NO_STR
+ CFG_MSDP_STR
+ "Configure MSDP peer\n"
+ "MSDP Peer address\n"
+ "SA access-list filter\n"
+ "SA access-list name\n"
+ "Filter incoming SAs\n"
+ "Filter outgoing SAs\n")
+{
+ const struct lyd_node *peer_node;
+ char xpath[XPATH_MAXLEN + 24];
+
+ snprintf(xpath, sizeof(xpath), "%s/msdp-peer[peer-ip='%s']",
+ VTY_CURR_XPATH, peer_str);
+ peer_node = yang_dnode_get(vty->candidate_config->dnode, xpath);
+ if (peer_node == NULL) {
+ vty_out(vty, "%% MSDP peer %s not yet configured\n", peer_str);
+ return CMD_SUCCESS;
+ }
+
+ if (strcmp(dir, "in") == 0)
+ nb_cli_enqueue_change(vty, "./sa-filter-in", NB_OP_DESTROY,
+ NULL);
+ else
+ nb_cli_enqueue_change(vty, "./sa-filter-out", NB_OP_DESTROY,
+ NULL);
+
+ return nb_cli_apply_changes(vty, "%s", xpath);
+}
+
DEFPY(pim_msdp_mesh_group_member,
pim_msdp_mesh_group_member_cmd,
"msdp mesh-group WORD$gname member A.B.C.D$maddr",
install_element(PIM_NODE, &no_pim_msdp_peer_cmd);
install_element(PIM_NODE, &pim_msdp_timers_cmd);
install_element(PIM_NODE, &no_pim_msdp_timers_cmd);
+ install_element(PIM_NODE, &msdp_peer_sa_filter_cmd);
+ install_element(PIM_NODE, &no_ip_msdp_peer_sa_filter_cmd);
install_element(PIM_NODE, &pim_msdp_mesh_group_member_cmd);
install_element(PIM_NODE, &no_pim_msdp_mesh_group_member_cmd);
install_element(PIM_NODE, &pim_msdp_mesh_group_source_cmd);
vty_out(vty, " msdp peer %pI4 source %pI4\n", &mp->peer,
&mp->local);
+
+ if (mp->acl_in)
+ vty_out(vty, " msdp peer %pI4 sa-filter %s in\n",
+ &mp->peer, mp->acl_in);
+
+ if (mp->acl_out)
+ vty_out(vty, " msdp peer %pI4 sa-filter %s out\n",
+ &mp->peer, mp->acl_out);
+
written = true;
}
/* timestamps */
int64_t uptime;
+
+ /** SA input access list name. */
+ char *acl_in;
+ /** SA output access list name. */
+ char *acl_out;
};
struct pim_msdp_mg_mbr {
#include <zebra.h>
#include <lib/log.h>
+#include <lib/filter.h>
#include <lib/network.h>
+#include <lib/prefix.h>
#include <lib/stream.h>
#include "frrevent.h"
#include <lib/vty.h>
pim_msdp_pkt_send(mp, s);
}
-static void pim_msdp_pkt_sa_push_to_one_peer(struct pim_instance *pim,
- struct pim_msdp_peer *mp)
+static void pim_msdp_pkt_sa_push(struct pim_instance *pim,
+ struct pim_msdp_peer *mp)
{
struct stream *s;
}
}
-/* push the stream into the obuf fifo of all the peers */
-static void pim_msdp_pkt_sa_push(struct pim_instance *pim,
- struct pim_msdp_peer *mp)
-{
- struct listnode *mpnode;
-
- if (mp) {
- pim_msdp_pkt_sa_push_to_one_peer(pim, mp);
- } else {
- for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, mpnode, mp)) {
- if (PIM_DEBUG_MSDP_INTERNAL) {
- zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push",
- mp->key_str);
- }
- pim_msdp_pkt_sa_push_to_one_peer(pim, mp);
- }
- }
-}
-
static int pim_msdp_pkt_sa_fill_hdr(struct pim_instance *pim, int local_cnt,
struct in_addr rp)
{
stream_put_ipv4(sa->pim->msdp.work_obuf, sa->sg.src.s_addr);
}
+static bool msdp_cisco_match(const struct filter *filter,
+ const struct in_addr *source,
+ const struct in_addr *group)
+{
+ const struct filter_cisco *cfilter = &filter->u.cfilter;
+ uint32_t source_addr;
+ uint32_t group_addr;
+
+ group_addr = group->s_addr & ~cfilter->mask_mask.s_addr;
+
+ if (cfilter->extended) {
+ source_addr = source->s_addr & ~cfilter->addr_mask.s_addr;
+ if (group_addr == cfilter->mask.s_addr &&
+ source_addr == cfilter->addr.s_addr)
+ return true;
+ } else if (group_addr == cfilter->addr.s_addr)
+ return true;
+
+ return false;
+}
+
+static enum filter_type msdp_access_list_apply(struct access_list *access,
+ const struct in_addr *source,
+ const struct in_addr *group)
+{
+ struct filter *filter;
+ struct prefix group_prefix;
+
+ if (access == NULL)
+ return FILTER_DENY;
+
+ for (filter = access->head; filter; filter = filter->next) {
+ if (filter->cisco) {
+ if (msdp_cisco_match(filter, source, group))
+ return filter->type;
+ } else {
+ group_prefix.family = AF_INET;
+ group_prefix.prefixlen = IPV4_MAX_BITLEN;
+ group_prefix.u.prefix4.s_addr = group->s_addr;
+ if (access_list_apply(access, &group_prefix))
+ return filter->type;
+ }
+ }
+
+ return FILTER_DENY;
+}
+
+bool msdp_peer_sa_filter(const struct pim_msdp_peer *mp,
+ const struct pim_msdp_sa *sa)
+{
+ struct access_list *acl;
+
+ /* No output filter configured, just quit. */
+ if (mp->acl_out == NULL)
+ return false;
+
+ /* Find access list and test it. */
+ acl = access_list_lookup(AFI_IP, mp->acl_out);
+ if (msdp_access_list_apply(acl, &sa->sg.src, &sa->sg.grp) == FILTER_DENY)
+ return true;
+
+ return false;
+}
+
+/** Count the number of SAs to be sent for a specific peer. */
+static size_t pim_msdp_peer_sa_count(const struct pim_instance *pim,
+ const struct pim_msdp_peer *peer)
+{
+ const struct pim_msdp_sa *sa;
+ const struct listnode *node;
+ size_t sa_count = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, node, sa)) {
+ if (!CHECK_FLAG(sa->flags, PIM_MSDP_SAF_LOCAL))
+ continue;
+ if (msdp_peer_sa_filter(peer, sa))
+ continue;
+
+ sa_count++;
+ }
+
+ return sa_count;
+}
+
static void pim_msdp_pkt_sa_gen(struct pim_instance *pim,
struct pim_msdp_peer *mp)
{
struct prefix group_all;
struct in_addr rp;
int sa_count;
- int local_cnt = pim->msdp.local_cnt;
+ int local_cnt = pim_msdp_peer_sa_count(pim, mp);
sa_count = 0;
if (PIM_DEBUG_MSDP_INTERNAL) {
* peers */
continue;
}
+
+ if (msdp_peer_sa_filter(mp, sa)) {
+ if (PIM_DEBUG_MSDP_EVENTS)
+ zlog_debug("MSDP peer %pI4 filter SA out %s",
+ &mp->peer, sa->sg_str);
+
+ continue;
+ }
+
/* add sa into scratch pad */
pim_msdp_pkt_sa_fill_one(sa);
++sa_count;
void pim_msdp_pkt_sa_tx(struct pim_instance *pim)
{
- pim_msdp_pkt_sa_gen(pim, NULL /* mp */);
+ struct pim_msdp_peer *mp;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, node, mp))
+ pim_msdp_pkt_sa_gen(pim, mp);
+
pim_msdp_pkt_sa_tx_done(pim);
}
void pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa)
{
+ struct pim_msdp_peer *mp;
+ struct listnode *node;
+
pim_msdp_pkt_sa_fill_hdr(sa->pim, 1 /* cnt */, sa->rp);
pim_msdp_pkt_sa_fill_one(sa);
- pim_msdp_pkt_sa_push(sa->pim, NULL);
+ for (ALL_LIST_ELEMENTS_RO(sa->pim->msdp.peer_list, node, mp)) {
+ if (msdp_peer_sa_filter(mp, sa)) {
+ if (PIM_DEBUG_MSDP_EVENTS)
+ zlog_debug("MSDP peer %pI4 filter SA out %s",
+ &mp->peer, sa->sg_str);
+ continue;
+ }
+
+ pim_msdp_pkt_sa_push(sa->pim, mp);
+ }
pim_msdp_pkt_sa_tx_done(sa->pim);
}
/* Fills the message contents. */
sa.pim = mp->pim;
sa.sg = sg;
+
+ /* Don't push it if filtered. */
+ if (msdp_peer_sa_filter(mp, &sa)) {
+ if (PIM_DEBUG_MSDP_EVENTS)
+ zlog_debug("MSDP peer %pI4 filter SA out (%pI4, %pI4)",
+ &mp->peer, &sa.sg.src, &sa.sg.grp);
+ return;
+ }
+
pim_msdp_pkt_sa_fill_one(&sa);
/* Pushes the message. */
static void pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer *mp, struct in_addr rp)
{
+ struct access_list *acl;
int prefix_len;
pim_sgaddr sg;
struct listnode *peer_node;
if (PIM_DEBUG_MSDP_PACKETS) {
zlog_debug(" sg %pSG", &sg);
}
+
+ /* Filter incoming SA with configured access list. */
+ if (mp->acl_in) {
+ acl = access_list_lookup(AFI_IP, mp->acl_in);
+ if (msdp_access_list_apply(acl, &sg.src, &sg.grp) ==
+ FILTER_DENY) {
+ if (PIM_DEBUG_MSDP_EVENTS)
+ zlog_debug("MSDP peer %pI4 filter SA in (%pI4, %pI4)",
+ &mp->peer, &sg.src, &sg.grp);
+ return;
+ }
+ }
+
pim_msdp_sa_ref(mp->pim, mp, &sg, rp);
/* Forwards the SA to the peers that are not in the RPF to the RP nor in
void pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp);
void pim_msdp_pkt_sa_tx_one_to_one_peer(struct pim_msdp_peer *mp,
struct in_addr rp, pim_sgaddr sg);
+bool msdp_peer_sa_filter(const struct pim_msdp_peer *mp,
+ const struct pim_msdp_sa *sa);
#endif
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_source_ip_modify,
}
},
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-in",
+ .cbs = {
+ .modify = pim_msdp_peer_sa_filter_in_modify,
+ .destroy = pim_msdp_peer_sa_filter_in_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-out",
+ .cbs = {
+ .modify = pim_msdp_peer_sa_filter_out_modify,
+ .destroy = pim_msdp_peer_sa_filter_out_destroy,
+ }
+ },
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mlag",
.cbs = {
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_source_ip_modify(
struct nb_cb_modify_args *args);
+int pim_msdp_peer_sa_filter_in_modify(struct nb_cb_modify_args *args);
+int pim_msdp_peer_sa_filter_in_destroy(struct nb_cb_destroy_args *args);
+int pim_msdp_peer_sa_filter_out_modify(struct nb_cb_modify_args *args);
+int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mlag_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mlag_destroy(
}
#endif /* PIM_IPV != 6 */
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-in
+ */
+int pim_msdp_peer_sa_filter_in_modify(struct nb_cb_modify_args *args)
+{
+ struct pim_msdp_peer *mp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ mp = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_TMP, mp->acl_in);
+ mp->acl_in = XSTRDUP(MTYPE_TMP,
+ yang_dnode_get_string(args->dnode, NULL));
+ break;
+ }
+
+ return NB_OK;
+}
+
+int pim_msdp_peer_sa_filter_in_destroy(struct nb_cb_destroy_args *args)
+{
+ struct pim_msdp_peer *mp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ mp = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_TMP, mp->acl_in);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer/sa-filter-out
+ */
+int pim_msdp_peer_sa_filter_out_modify(struct nb_cb_modify_args *args)
+{
+ struct pim_msdp_peer *mp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ mp = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_TMP, mp->acl_out);
+ mp->acl_out = XSTRDUP(MTYPE_TMP,
+ yang_dnode_get_string(args->dnode, NULL));
+ break;
+ }
+
+ return NB_OK;
+}
+
+int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args)
+{
+ struct pim_msdp_peer *mp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ mp = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_TMP, mp->acl_out);
+ break;
+ }
+
+ return NB_OK;
+}
+
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/mlag
*/