frr/pbrd/pbr_event.c
Donald Sharp e5c83d9b31 pbrd: Add PBR to FRR
This is an implementation of PBR for FRR.

This implemenation uses a combination of rules and
tables to determine how packets will flow.

PBR introduces a new concept of 'nexthop-groups' to
specify a group of nexthops that will be used for
ecmp.  Nexthop-groups are specified on the cli via:

nexthop-group DONNA
  nexthop 192.168.208.1
  nexthop 192.168.209.1
  nexthop 192.168.210.1
!

PBR sees the nexthop-group and installs these as a default
route with these nexthops starting at table 10000
robot# show pbr nexthop-groups
Nexthop-Group: DONNA Table: 10001 Valid: 1 Installed: 1
	Valid: 1  nexthop 192.168.209.1
	Valid: 1  nexthop 192.168.210.1
	Valid: 1  nexthop 192.168.208.1

I have also introduced the ability to specify a table
in a 'show ip route table XXX' to see the specified tables.

robot# show ip route table 10001
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, P - PIM, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR,
       > - selected route, * - FIB route

F>* 0.0.0.0/0 [0/0] via 192.168.208.1, enp0s8, 00:14:25
  *                 via 192.168.209.1, enp0s9, 00:14:25
  *                 via 192.168.210.1, enp0s10, 00:14:25

PBR tracks PBR-MAPS via the pbr-map command:

!
pbr-map EVA seq 10
  match src-ip 4.3.4.0/24
  set nexthop-group DONNA
!
pbr-map EVA seq 20
  match dst-ip 4.3.5.0/24
  set nexthop-group DONNA
!

pbr-maps can have 'match src-ip <prefix>' and 'match dst-ip <prefix>'
to affect decisions about incoming packets.  Additionally if you
only have one nexthop to use for a pbr-map you do not need
to setup a nexthop-group and can specify 'set nexthop XXXX'.

To apply the pbr-map to an incoming interface you do this:

interface enp0s10
 pbr-policy EVA
!

When a pbr-map is applied to interfaces it can be installed
into the kernel as a rule:

[sharpd@robot frr1]$ ip rule show
0:	from all lookup local
309:	from 4.3.4.0/24 iif enp0s10 lookup 10001
319:	from all to 4.3.5.0/24 iif enp0s10 lookup 10001
1000:	from all lookup [l3mdev-table]
32766:	from all lookup main
32767:	from all lookup default

[sharpd@robot frr1]$ ip route show table 10001
default proto pbr metric 20
	nexthop via 192.168.208.1 dev enp0s8 weight 1
	nexthop via 192.168.209.1 dev enp0s9 weight 1
	nexthop via 192.168.210.1 dev enp0s10 weight 1

The linux kernel now will use the rules and tables to properly
apply these policies.

Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
Signed-off-by: Don Slice <dslice@cumulusnetworks.com>
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
2018-04-06 13:22:43 -04:00

220 lines
5.7 KiB
C

/*
* PBR-event Code
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
*
* This file is part of FRR.
*
* 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 <thread.h>
#include <workqueue.h>
#include <nexthop.h>
#include <log.h>
#include <vty.h>
#include "pbrd/pbr_event.h"
#include "pbrd/pbr_map.h"
#include "pbrd/pbr_nht.h"
#include "pbrd/pbr_memory.h"
#include "pbrd/pbr_debug.h"
DEFINE_MTYPE_STATIC(PBRD, PBR_EVENT, "Event WorkQueue")
struct work_queue *pbr_event_wq;
static const char *pbr_event_wqentry2str(struct pbr_event *pbre,
char *buffer, size_t buflen)
{
switch(pbre->event) {
case PBR_NHG_NEW:
snprintf(buffer, buflen, "Nexthop Group Added %s",
pbre->name);
break;
case PBR_NHG_ADD_NEXTHOP:
snprintf(buffer, buflen, "Nexthop Group Nexthop Added %s",
pbre->name);
break;
case PBR_NHG_DEL_NEXTHOP:
snprintf(buffer, buflen, "Nexthop Group Nexthop Deleted %s",
pbre->name);
break;
case PBR_NHG_DELETE:
snprintf(buffer, buflen, "Nexthop Group Deleted %s",
pbre->name);
break;
case PBR_MAP_NEXTHOP_ADD:
snprintf(buffer, buflen, "Nexthop Added to %s(%d)", pbre->name,
pbre->seqno);
break;
case PBR_MAP_NEXTHOP_DELETE:
snprintf(buffer, buflen, "Nexthop Deleted from %s(%d)",
pbre->name, pbre->seqno);
break;
case PBR_MAP_NHG_ADD:
snprintf(buffer, buflen, "Nexthop Group Added to %s(%d)",
pbre->name, pbre->seqno);
break;
case PBR_MAP_NHG_DELETE:
snprintf(buffer, buflen, "Nexthop Group Deleted from %s(%d)",
pbre->name, pbre->seqno);
break;
case PBR_MAP_ADD:
snprintf(buffer, buflen, "PBR-MAP %s Added",
pbre->name);
break;
case PBR_MAP_MODIFY:
snprintf(buffer, buflen, "PBR_MAP %s Modified",
pbre->name);
break;
case PBR_MAP_DELETE:
snprintf(buffer, buflen, "PBR_MAP %s Deleted",
pbre->name);
break;
case PBR_NH_CHANGED:
snprintf(buffer, buflen, "Nexthop Call back from Zebra");
break;
case PBR_MAP_INSTALL:
snprintf(buffer, buflen, "PBR_MAP %s Installing into zapi",
pbre->name);
break;
case PBR_POLICY_CHANGED:
snprintf(buffer, buflen,
"PBR-Policy %s applied to an interface", pbre->name);
break;
case PBR_MAP_POLICY_INSTALL:
snprintf(buffer, buflen, "PBR-POLICY installation time for %s",
pbre->name);
break;
case PBR_POLICY_DELETED:
snprintf(buffer, buflen, "PBR-POLICY deleted from %s",
pbre->name);
break;
}
return buffer;
}
void pbr_event_free(struct pbr_event **pbre)
{
XFREE(MTYPE_PBR_EVENT, *pbre);
}
static void pbr_event_delete_wq(struct work_queue *wq, void *data)
{
struct pbr_event *pbre = (struct pbr_event *)data;
XFREE(MTYPE_PBR_EVENT, pbre);
}
static wq_item_status pbr_event_process_wq(struct work_queue *wq, void *data)
{
struct pbr_event *pbre = (struct pbr_event *)data;
char buffer[256];
DEBUGD(&pbr_dbg_event, "%s: Handling event %s", __PRETTY_FUNCTION__,
pbr_event_wqentry2str(pbre, buffer, sizeof(buffer)));
switch (pbre->event) {
case PBR_NHG_NEW:
pbr_nht_add_group(pbre->name);
pbr_map_check_nh_group_change(pbre->name);
break;
case PBR_NHG_ADD_NEXTHOP:
pbr_nht_change_group(pbre->name);
pbr_map_check_nh_group_change(pbre->name);
break;
case PBR_NHG_DEL_NEXTHOP:
pbr_nht_change_group(pbre->name);
pbr_map_check_nh_group_change(pbre->name);
break;
case PBR_NHG_DELETE:
pbr_nht_delete_group(pbre->name);
pbr_map_check_nh_group_change(pbre->name);
break;
case PBR_MAP_NEXTHOP_ADD:
pbr_nht_add_individual_nexthop(pbre->name, pbre->seqno);
pbr_map_check(pbre->name, pbre->seqno);
break;
case PBR_MAP_NEXTHOP_DELETE:
pbr_nht_delete_individual_nexthop(pbre->name, pbre->seqno);
pbr_map_check(pbre->name, pbre->seqno);
break;
case PBR_MAP_NHG_ADD:
pbr_map_check(pbre->name, pbre->seqno);
break;
case PBR_MAP_NHG_DELETE:
pbr_map_check(pbre->name, pbre->seqno);
break;
case PBR_MAP_ADD:
pbr_map_add_interfaces(pbre->name);
break;
case PBR_MAP_MODIFY:
pbr_map_check(pbre->name, pbre->seqno);
break;
case PBR_MAP_DELETE:
pbr_map_delete(pbre->name, pbre->seqno);
break;
case PBR_NH_CHANGED:
pbr_map_check_nh_group_change(pbre->name);
break;
case PBR_MAP_INSTALL:
pbr_map_install(pbre->name);
break;
case PBR_POLICY_CHANGED:
pbr_map_check_policy_change(pbre->name);
break;
case PBR_MAP_POLICY_INSTALL:
pbr_map_policy_install(pbre->name);
break;
case PBR_POLICY_DELETED:
pbr_map_policy_delete(pbre->name);
break;
}
return WQ_SUCCESS;
}
void pbr_event_enqueue(struct pbr_event *pbre)
{
work_queue_add(pbr_event_wq, pbre);
}
struct pbr_event *pbr_event_new(enum pbr_events ev, const char *name)
{
struct pbr_event *event;
event = XCALLOC(MTYPE_PBR_EVENT, sizeof(struct pbr_event));
event->event = ev;
if (name)
strlcpy(event->name, name, sizeof(event->name));
return event;
}
extern struct thread_master *master;
void pbr_event_init(void)
{
pbr_event_wq = work_queue_new(master, "PBR Main Work Queue");
pbr_event_wq->spec.workfunc = &pbr_event_process_wq;
pbr_event_wq->spec.del_item_data = &pbr_event_delete_wq;
}
void pbr_event_stop(void)
{
work_queue_free_and_null(&pbr_event_wq);
}