pkgrel=0
pkgdesc="Free Range Routing is a fork of quagga"
url="https://frrouting.org/"
-arch="all"
+arch="x86_64"
license="GPL-2.0"
-depends="iproute2 json-c c-ares ipsec-tools iproute2"
+depends="json-c c-ares ipsec-tools iproute2 python py-ipaddr bash"
makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
acct autoconf automake bash
binutils binutils-libs bison bsd-compat-headers build-base
patch pax-utils pcre perl pkgconf python2 python2-dev readline
readline-dev sqlite-libs squashfs-tools sudo tar texinfo xorriso xz-libs
py-sphinx"
+install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall"
subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
-source="$pkgname-$pkgver.tar.gz"
+source="$pkgname-$pkgver.tar.gz docker-start daemons daemons.conf"
builddir="$srcdir"/$pkgname-$pkgver
+_sbindir=/usr/lib/frr
+_sysconfdir=/etc/frr
+_libdir=/usr/lib
+_localstatedir=/var/run/frr
+_user=frr
+
build() {
cd "$builddir"
- ./configure --prefix=/usr || return 1
+ ./configure \
+ --prefix=/usr \
+ --sbindir=$_sbindir \
+ --sysconfdir=$_sysconfdir \
+ --libdir=$_libdir \
+ --localstatedir=$_localstatedir \
+ --enable-systemd=no \
+ --enable-vtysh \
+ --enable-multipath=64 \
+ --enable-vty-group=frrvty \
+ --enable-user=$_user \
+ --enable-group=$_user || return 1
make || return 1
}
package() {
cd "$builddir"
make DESTDIR="$pkgdir" install || return 1
+
+ install -Dm755 "$srcdir"/docker-start "$pkgdir"$_sbindir
+ install -Dm644 "$srcdir"/daemons "$pkgdir"$_sysconfdir
+ install -Dm644 "$srcdir"/daemons.conf "$pkgdir"$_sysconfdir
+ install -d "$pkgdir"/etc/init.d
+ ln -s ${_sbindir}/frr "$pkgdir"/etc/init.d/frr
}
--- /dev/null
+#!/bin/sh
+
+set -e
+
+##
+# For volume mounts...
+##
+chown -R frr:frr /etc/frr
+/etc/init.d/frr start
+exec sleep 10000d
--- /dev/null
+#!/bin/sh
+
+getent passwd frr > /dev/null && deluser frr
+getent group frrvty > /dev/null && delgroup frrvty
+getent group frr > /dev/null && delgroup frr
+exit 0
--- /dev/null
+#!/bin/sh
+
+/etc/init.d/frr stop
+exit 0
--- /dev/null
+#!/bin/sh
+
+for g in frr frrvty; do
+ ! getent group $g > /dev/null && addgroup -S $g
+done
+
+! getent passwd frr > /dev/null && \
+ adduser -S -D -h /var/run/frr -s /sbin/nologin -G frr -g frr frr
+
+adduser frr frrvty
resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots);
if(route_slots >= max_route_slots)
return NULL;
+ assert(routes);
route->next = NULL;
if(n < route_slots)
memmove(routes + n + 1, routes + n,
bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \
bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \
bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \
- bgp_flowspec_vty.c
+ bgp_flowspec_vty.c bgp_labelpool.c
noinst_HEADERS = \
bgp_memory.h \
bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \
$(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \
bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \
- bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h
+ bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h \
+ bgp_labelpool.h
bgpd_SOURCES = bgp_main.c
bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@
unsigned long eth_tag_id;
struct attr;
+/* EVPN ESI */
struct eth_segment_id {
uint8_t val[ESI_LEN];
};
/* Unset community-list */
int community_list_unset(struct community_list_handler *ch, const char *name,
- const char *str, int direct, int style, int delete_all)
+ const char *str, int direct, int style)
{
struct community_entry *entry = NULL;
struct community_list *list;
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
/* Delete all of entry belongs to this community-list. */
- if (delete_all) {
+ if (!str) {
community_list_delete(list);
route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
return 0;
}
- if (style == COMMUNITY_LIST_STANDARD) {
- if (str)
- com = community_str2com(str);
- }
+ if (style == COMMUNITY_LIST_STANDARD)
+ com = community_str2com(str);
if (com) {
entry = community_list_entry_lookup(list, com, direct);
return 0;
}
-/* Unset extcommunity-list. When str is NULL, delete all of
- extcommunity-list entry belongs to the specified name. */
+/* Unset extcommunity-list.
+ *
+ * When str is NULL, delete all extcommunity-list entries belonging to the
+ * specified name.
+ */
int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
- const char *str, int direct, int style,
- int delete_all)
+ const char *str, int direct, int style)
{
struct community_entry *entry = NULL;
struct community_list *list;
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
/* Delete all of entry belongs to this extcommunity-list. */
- if (delete_all) {
+ if (!str) {
community_list_delete(list);
route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
return 0;
}
- if (style == EXTCOMMUNITY_LIST_STANDARD) {
- if (str)
- ecom = ecommunity_str2com(str, 0, 1);
- }
+ if (style == EXTCOMMUNITY_LIST_STANDARD)
+ ecom = ecommunity_str2com(str, 0, 1);
if (ecom) {
entry = community_list_entry_lookup(list, ecom, direct);
int style);
extern int community_list_unset(struct community_list_handler *ch,
const char *name, const char *str, int direct,
- int style, int delete_all);
+ int style);
extern int extcommunity_list_set(struct community_list_handler *ch,
const char *name, const char *str, int direct,
int style);
extern int extcommunity_list_unset(struct community_list_handler *ch,
const char *name, const char *str,
- int direct, int style, int delete_all);
+ int direct, int style);
extern int lcommunity_list_set(struct community_list_handler *ch,
const char *name, const char *str, int direct,
int style);
/* Remove the route from a reuse list if it is on one. */
if (CHECK_FLAG(bdi->binfo->flags, BGP_INFO_DAMPED)) {
/* If decay rate isn't equal to 0, reinsert brn. */
- if (bdi->penalty != last_penalty) {
+ if (bdi->penalty != last_penalty && bdi->index >= 0) {
bgp_reuse_list_delete(bdi);
bgp_reuse_list_add(bdi);
}
unsigned long conf_bgp_debug_update_groups;
unsigned long conf_bgp_debug_vpn;
unsigned long conf_bgp_debug_flowspec;
+unsigned long conf_bgp_debug_labelpool;
unsigned long term_bgp_debug_as4;
unsigned long term_bgp_debug_neighbor_events;
unsigned long term_bgp_debug_update_groups;
unsigned long term_bgp_debug_vpn;
unsigned long term_bgp_debug_flowspec;
+unsigned long term_bgp_debug_labelpool;
struct list *bgp_debug_neighbor_events_peers = NULL;
struct list *bgp_debug_keepalive_peers = NULL;
return CMD_SUCCESS;
}
+DEFUN (debug_bgp_labelpool,
+ debug_bgp_labelpool_cmd,
+ "debug bgp labelpool",
+ DEBUG_STR
+ BGP_STR
+ "label pool\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(labelpool, LABELPOOL);
+ else
+ TERM_DEBUG_ON(labelpool, LABELPOOL);
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "enabled debug bgp labelpool\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_labelpool,
+ no_debug_bgp_labelpool_cmd,
+ "no debug bgp labelpool",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "label pool\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(labelpool, LABELPOOL);
+ else
+ TERM_DEBUG_OFF(labelpool, LABELPOOL);
+
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "disabled debug bgp labelpool\n");
+
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_bgp,
no_debug_bgp_cmd,
"no debug bgp",
TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
TERM_DEBUG_OFF(flowspec, FLOWSPEC);
+ TERM_DEBUG_OFF(labelpool, LABELPOOL);
vty_out(vty, "All possible debugging has been turned off\n");
return CMD_SUCCESS;
vty_out(vty, " BGP vpn label event debugging is on\n");
if (BGP_DEBUG(flowspec, FLOWSPEC))
vty_out(vty, " BGP flowspec debugging is on\n");
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ vty_out(vty, " BGP labelpool debugging is on\n");
vty_out(vty, "\n");
return CMD_SUCCESS;
ret++;
if (BGP_DEBUG(flowspec, FLOWSPEC))
ret++;
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ ret++;
return ret;
}
vty_out(vty, "debug bgp flowspec\n");
write++;
}
+ if (CONF_BGP_DEBUG(labelpool, LABELPOOL)) {
+ vty_out(vty, "debug bgp labelpool\n");
+ write++;
+ }
return write;
}
install_element(CONFIG_NODE, &debug_bgp_vpn_cmd);
install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd);
install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_labelpool_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd);
}
/* Return true if this prefix is on the per_prefix_list of prefixes to debug
extern unsigned long conf_bgp_debug_update_groups;
extern unsigned long conf_bgp_debug_vpn;
extern unsigned long conf_bgp_debug_flowspec;
+extern unsigned long conf_bgp_debug_labelpool;
extern unsigned long term_bgp_debug_as4;
extern unsigned long term_bgp_debug_neighbor_events;
extern unsigned long term_bgp_debug_update_groups;
extern unsigned long term_bgp_debug_vpn;
extern unsigned long term_bgp_debug_flowspec;
+extern unsigned long term_bgp_debug_labelpool;
extern struct list *bgp_debug_neighbor_events_peers;
extern struct list *bgp_debug_keepalive_peers;
#define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04
#define BGP_DEBUG_VPN_LEAK_LABEL 0x08
#define BGP_DEBUG_FLOWSPEC 0x01
+#define BGP_DEBUG_LABELPOOL 0x01
#define BGP_DEBUG_PACKET_SEND 0x01
#define BGP_DEBUG_PACKET_SEND_DETAIL 0x02
"MM:%u", seqnum);
} else
unk_ecom = 1;
+ } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
+ sub_type = *pnt++;
+ if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
+ len = sprintf(
+ str_buf + str_pnt,
+ "FS:redirect IP 0x%x", *(pnt+5));
+ } else
+ unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_TRANS_EXP) {
sub_type = *pnt++;
len = sprintf(
str_buf + str_pnt,
"FS:marking %u", *(pnt+5));
- } else if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
- len = sprintf(
- str_buf + str_pnt,
- "FS:redirect IP 0x%x", *(pnt+5));
} else
unk_ecom = 1;
} else
#define ECOMMUNITY_ENCODE_OPAQUE 0x03
#define ECOMMUNITY_ENCODE_EVPN 0x06
#define ECOMMUNITY_ENCODE_TRANS_EXP 0x80 /* Flow Spec */
+#define ECOMMUNITY_ENCODE_REDIRECT_IP_NH 0x08 /* Flow Spec */
/* RFC7674 */
#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 0x81
#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_3 0x82
{
struct prefix_rd prd;
struct prefix_evpn p;
+ struct bgp_route_evpn evpn;
uint8_t ipaddr_len;
uint8_t macaddr_len;
mpls_label_t label[BGP_MAX_LABELS]; /* holds the VNI(s) as in packet */
uint32_t num_labels = 0;
+ uint32_t eth_tag;
int ret;
/* Type-2 route should be either 33, 37 or 49 bytes or an
return -1;
}
+ memset(&evpn, 0, sizeof(evpn));
+
/* Make prefix_rd */
prd.family = AF_UNSPEC;
prd.prefixlen = 64;
p.prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
- /* Skip over Ethernet Seg Identifier for now. */
- pfx += 10;
+ /* Copy Ethernet Seg Identifier */
+ memcpy(&evpn.eth_s_id.val, pfx, ESI_LEN);
+ pfx += ESI_LEN;
- /* Skip over Ethernet Tag for now. */
+ /* Copy Ethernet Tag */
+ memcpy(ð_tag, pfx, 4);
+ p.prefix.eth_tag = ntohl(eth_tag);
pfx += 4;
/* Get the MAC Addr len */
if (attr)
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, &label[0], num_labels, 0, NULL);
+ &prd, &label[0], num_labels, 0, &evpn);
else
ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, &label[0], num_labels, NULL);
+ &prd, &label[0], num_labels, &evpn);
return ret;
}
struct prefix_rd prd;
struct prefix_evpn p;
uint8_t ipaddr_len;
+ uint32_t eth_tag;
int ret;
/* Type-3 route should be either 17 or 29 bytes: RD (8), Eth Tag (4),
p.prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IMET_ROUTE;
- /* Skip over Ethernet Tag for now. */
+ /* Copy Ethernet Tag */
+ memcpy(ð_tag, pfx, 4);
+ p.prefix.eth_tag = ntohl(eth_tag);
pfx += 4;
/* Get the IP. */
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
json_object_int_add(json, "routeType", p->prefix.route_type);
- json_object_int_add(json, "ethTag", 0);
+ json_object_int_add(json, "ethTag", p->prefix.eth_tag);
json_object_int_add(json, "ipLen",
IS_EVPN_PREFIX_IPADDR_V4(p)
? IPV4_MAX_BITLEN
if (IS_EVPN_PREFIX_IPADDR_NONE(p)) {
json_object_int_add(json, "routeType",
p->prefix.route_type);
- json_object_int_add(
- json, "esi",
- 0); /* TODO: we don't support esi yet */
- json_object_int_add(json, "ethTag", 0);
+ json_object_int_add(json, "ethTag", p->prefix.eth_tag);
json_object_int_add(json, "macLen", 8 * ETH_ALEN);
json_object_string_add(json, "mac",
prefix_mac2str(&p->prefix.mac,
json_object_int_add(json, "routeType",
p->prefix.route_type);
- json_object_int_add(
- json, "esi",
- 0); /* TODO: we don't support esi yet */
- json_object_int_add(json, "ethTag", 0);
+ json_object_int_add(json, "ethTag", p->prefix.eth_tag);
json_object_int_add(json, "macLen", 8 * ETH_ALEN);
json_object_string_add(json, "mac",
prefix_mac2str(&p->prefix.mac,
char buf2[PREFIX2STR_BUFFER];
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
- snprintf(buf, len, "[%d]:[0]:[%d]:[%s]", p->prefix.route_type,
+ snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type,
+ p->prefix.eth_tag,
IS_EVPN_PREFIX_IPADDR_V4(p) ? IPV4_MAX_BITLEN
: IPV6_MAX_BITLEN,
inet_ntoa(p->prefix.ip.ipaddr_v4));
} else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
if (IS_EVPN_PREFIX_IPADDR_NONE(p))
- snprintf(buf, len, "[%d]:[0]:[0]:[%d]:[%s]",
- p->prefix.route_type, 8 * ETH_ALEN,
+ snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]",
+ p->prefix.route_type,
+ p->prefix.eth_tag,
+ 8 * ETH_ALEN,
prefix_mac2str(&p->prefix.mac, buf1,
sizeof(buf1)));
else {
family = IS_EVPN_PREFIX_IPADDR_V4(p) ? AF_INET
: AF_INET6;
- snprintf(buf, len, "[%d]:[0]:[0]:[%d]:[%s]:[%d]:[%s]",
- p->prefix.route_type, 8 * ETH_ALEN,
+ snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]:[%d]:[%s]",
+ p->prefix.route_type,
+ p->prefix.eth_tag,
+ 8 * ETH_ALEN,
prefix_mac2str(&p->prefix.mac, buf1,
sizeof(buf1)),
family == AF_INET ? IPV4_MAX_BITLEN
PREFIX2STR_BUFFER));
}
} else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
- snprintf(buf, len, "[%d]:[0]:[0]:[%d]:[%s]",
- p->prefix.route_type, p->prefix.ip_prefix_length,
+ snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]",
+ p->prefix.route_type,
+ p->prefix.eth_tag,
+ p->prefix.ip_prefix_length,
IS_EVPN_PREFIX_IPADDR_V4(p)
? inet_ntoa(p->prefix.ip.ipaddr_v4)
: inet6_ntoa(p->prefix.ip.ipaddr_v6));
len += 3;
stream_putc(s, len);
stream_put(s, prd->val, 8); /* RD */
- stream_put(s, 0, 10); /* ESI */
- stream_putl(s, 0); /* Ethernet Tag ID */
+ if (attr)
+ stream_put(s, &attr->evpn_overlay.eth_s_id, ESI_LEN);
+ else
+ stream_put(s, 0, 10);
+ stream_putl(s, evp->prefix.eth_tag); /* Ethernet Tag ID */
stream_putc(s, 8 * ETH_ALEN); /* Mac Addr Len - bits */
stream_put(s, evp->prefix.mac.octet, 6); /* Mac Addr */
stream_putc(s, 8 * ipa_len); /* IP address Length */
case BGP_EVPN_IMET_ROUTE:
stream_putc(s, 17); // TODO: length - assumes IPv4 address
stream_put(s, prd->val, 8); /* RD */
- stream_putl(s, 0); /* Ethernet Tag ID */
+ stream_putl(s, evp->prefix.eth_tag); /* Ethernet Tag ID */
stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */
/* Originating Router's IP Addr */
stream_put_in_addr(s, &evp->prefix.ip.ipaddr_v4);
"* valid, > best, i - internal\n");
vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n");
vty_out(vty,
- "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
+ "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
- vty_out(vty, "EVPN type-5 prefix: [5]:[ESI]:[EthTag]:[IPlen]:[IP]\n\n");
+ vty_out(vty, "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
vty_out(vty, "%s", ri_header);
}
/* RD header and legend - once overall. */
if (rd_header && !json) {
vty_out(vty,
- "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n");
+ "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n");
vty_out(vty,
"EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
vty_out(vty,
- "EVPN type-5 prefix: [5]:[ESI]:[EthTag]:[IPlen]:[IP]\n\n");
+ "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
rd_header = 0;
}
json_object_array_add(json_paths,
json_ecom_path);
}
+ if (attr->nexthop.s_addr != 0 &&
+ display == NLRI_STRING_FORMAT_LARGE)
+ vty_out(vty, "\tNH %-16s\n", inet_ntoa(attr->nexthop));
XFREE(MTYPE_ECOMMUNITY_STR, s);
}
peer_uptime(binfo->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL);
--- /dev/null
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program 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 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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 "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "mpls.h"
+#include "vty.h"
+#include "fifo.h"
+#include "linklist.h"
+#include "skiplist.h"
+#include "workqueue.h"
+#include "zclient.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_debug.h"
+
+/*
+ * Definitions and external declarations.
+ */
+extern struct zclient *zclient;
+
+/*
+ * Remember where pool data are kept
+ */
+static struct labelpool *lp;
+
+/* request this many labels at a time from zebra */
+#define LP_CHUNK_SIZE 50
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback")
+
+#define LABEL_FIFO_ADD(F, N) \
+ do { \
+ FIFO_ADD((F), (N)); \
+ (F)->count++; \
+ } while (0)
+
+#define LABEL_FIFO_DEL(F, N) \
+ do { \
+ FIFO_DEL((N)); \
+ (F)->count--; \
+ } while (0)
+
+#define LABEL_FIFO_INIT(F) \
+ do { \
+ FIFO_INIT((F)); \
+ (F)->count = 0; \
+ } while (0)
+
+#define LABEL_FIFO_COUNT(F) ((F)->count)
+
+#define LABEL_FIFO_EMPTY(F) FIFO_EMPTY(F)
+
+#define LABEL_FIFO_HEAD(F) ((F)->next == (F) ? NULL : (F)->next)
+
+struct lp_chunk {
+ uint32_t first;
+ uint32_t last;
+};
+
+/*
+ * label control block
+ */
+struct lp_lcb {
+ mpls_label_t label; /* MPLS_LABEL_NONE = not allocated */
+ int type;
+ void *labelid; /* unique ID */
+ /*
+ * callback for label allocation and loss
+ *
+ * allocated: false = lost
+ */
+ int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+};
+
+/* XXX same first elements as "struct fifo" */
+struct lp_fifo {
+ struct lp_fifo *next;
+ struct lp_fifo *prev;
+
+ uint32_t count;
+ struct lp_lcb lcb;
+};
+
+struct lp_cbq_item {
+ int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+ int type;
+ mpls_label_t label;
+ void *labelid;
+ bool allocated; /* false = lost */
+};
+
+static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data)
+{
+ struct lp_cbq_item *lcbq = data;
+ int rc;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ if (debug)
+ zlog_debug("%s: calling callback with labelid=%p label=%u allocated=%d",
+ __func__, lcbq->labelid, lcbq->label, lcbq->allocated);
+
+ if (lcbq->label == MPLS_LABEL_NONE) {
+ /* shouldn't happen */
+ zlog_err("%s: error: label==MPLS_LABEL_NONE", __func__);
+ return WQ_SUCCESS;
+ }
+
+ rc = (*(lcbq->cbfunc))(lcbq->label, lcbq->labelid, lcbq->allocated);
+
+ if (lcbq->allocated && rc) {
+ /*
+ * Callback rejected allocation. This situation could arise
+ * if there was a label request followed by the requestor
+ * deciding it didn't need the assignment (e.g., config
+ * change) while the reply to the original request (with
+ * label) was in the work queue.
+ */
+ if (debug)
+ zlog_debug("%s: callback rejected allocation, releasing labelid=%p label=%u",
+ __func__, lcbq->labelid, lcbq->label);
+
+ uintptr_t lbl = lcbq->label;
+ void *labelid;
+ struct lp_lcb *lcb;
+
+ /*
+ * If the rejected label was marked inuse by this labelid,
+ * release the label back to the pool.
+ *
+ * Further, if the rejected label was still assigned to
+ * this labelid in the LCB, delete the LCB.
+ */
+ if (!skiplist_search(lp->inuse, (void *)lbl, &labelid)) {
+ if (labelid == lcbq->labelid) {
+ if (!skiplist_search(lp->ledger, labelid,
+ (void **)&lcb)) {
+ if (lcbq->label == lcb->label)
+ skiplist_delete(lp->ledger,
+ labelid, NULL);
+ }
+ skiplist_delete(lp->inuse, (void *)lbl, NULL);
+ }
+ }
+ }
+
+ return WQ_SUCCESS;
+}
+
+static void lp_cbq_item_free(struct work_queue *wq, void *data)
+{
+ XFREE(MTYPE_BGP_LABEL_CBQ, data);
+}
+
+static void lp_lcb_free(void *goner)
+{
+ if (goner)
+ XFREE(MTYPE_BGP_LABEL_CB, goner);
+}
+
+static void lp_chunk_free(void *goner)
+{
+ if (goner)
+ XFREE(MTYPE_BGP_LABEL_CHUNK, goner);
+}
+
+void bgp_lp_init(struct thread_master *master, struct labelpool *pool)
+{
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug("%s: entry", __func__);
+
+ lp = pool; /* Set module pointer to pool data */
+
+ lp->ledger = skiplist_new(0, NULL, lp_lcb_free);
+ lp->inuse = skiplist_new(0, NULL, NULL);
+ lp->chunks = list_new();
+ lp->chunks->del = lp_chunk_free;
+ lp->requests = XCALLOC(MTYPE_BGP_LABEL_FIFO, sizeof(struct lp_fifo));
+ LABEL_FIFO_INIT(lp->requests);
+ lp->callback_q = work_queue_new(master, "label callbacks");
+ if (!lp->callback_q) {
+ zlog_err("%s: Failed to allocate work queue", __func__);
+ exit(1);
+ }
+
+ lp->callback_q->spec.workfunc = lp_cbq_docallback;
+ lp->callback_q->spec.del_item_data = lp_cbq_item_free;
+ lp->callback_q->spec.max_retries = 0;
+}
+
+void bgp_lp_finish(void)
+{
+ struct lp_fifo *lf;
+
+ if (!lp)
+ return;
+
+ skiplist_free(lp->ledger);
+ lp->ledger = NULL;
+
+ skiplist_free(lp->inuse);
+ lp->inuse = NULL;
+
+ list_delete_and_null(&lp->chunks);
+
+ while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
+
+ LABEL_FIFO_DEL(lp->requests, lf);
+ XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+ }
+ XFREE(MTYPE_BGP_LABEL_FIFO, lp->requests);
+ lp->requests = NULL;
+
+ work_queue_free_and_null(&lp->callback_q);
+
+ lp = NULL;
+}
+
+static mpls_label_t get_label_from_pool(void *labelid)
+{
+ struct listnode *node;
+ struct lp_chunk *chunk;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ /*
+ * Find a free label
+ * Linear search is not efficient but should be executed infrequently.
+ */
+ for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) {
+ uintptr_t lbl;
+
+ if (debug)
+ zlog_debug("%s: chunk first=%u last=%u",
+ __func__, chunk->first, chunk->last);
+
+ for (lbl = chunk->first; lbl <= chunk->last; ++lbl) {
+ /* labelid is key to all-request "ledger" list */
+ if (!skiplist_insert(lp->inuse, (void *)lbl, labelid)) {
+ /*
+ * Success
+ */
+ return lbl;
+ }
+ }
+ }
+ return MPLS_LABEL_NONE;
+}
+
+/*
+ * Success indicated by value of "label" field in returned LCB
+ */
+static struct lp_lcb *lcb_alloc(
+ int type,
+ void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+ /*
+ * Set up label control block
+ */
+ struct lp_lcb *new = XCALLOC(MTYPE_BGP_LABEL_CB,
+ sizeof(struct lp_lcb));
+
+ new->label = get_label_from_pool(labelid);
+ new->type = type;
+ new->labelid = labelid;
+ new->cbfunc = cbfunc;
+
+ return new;
+}
+
+/*
+ * Callers who need labels must supply a type, labelid, and callback.
+ * The type is a value defined in bgp_labelpool.h (add types as needed).
+ * The callback is for asynchronous notification of label allocation.
+ * The labelid is passed as an argument to the callback. It should be unique
+ * to the requested label instance.
+ *
+ * If zebra is not connected, callbacks with labels will be delayed
+ * until connection is established. If zebra connection is lost after
+ * labels have been assigned, existing assignments via this labelpool
+ * module will continue until reconnection.
+ *
+ * When connection to zebra is reestablished, previous label assignments
+ * will be invalidated (via callbacks having the "allocated" parameter unset)
+ * and new labels will be automatically reassigned by this labelpool module
+ * (that is, a requestor does not need to call lp_get() again if it is
+ * notified via callback that its label has been lost: it will eventually
+ * get another callback with a new label assignment).
+ *
+ * Prior requests for a given labelid are detected so that requests and
+ * assignments are not duplicated.
+ */
+void bgp_lp_get(
+ int type,
+ void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+ struct lp_lcb *lcb;
+ int requested = 0;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ if (debug)
+ zlog_debug("%s: labelid=%p", __func__, labelid);
+
+ /*
+ * Have we seen this request before?
+ */
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ requested = 1;
+ } else {
+ lcb = lcb_alloc(type, labelid, cbfunc);
+ if (debug)
+ zlog_debug("%s: inserting lcb=%p label=%u",
+ __func__, lcb, lcb->label);
+ int rc = skiplist_insert(lp->ledger, labelid, lcb);
+
+ if (rc) {
+ /* shouldn't happen */
+ zlog_err("%s: can't insert new LCB into ledger list",
+ __func__);
+ XFREE(MTYPE_BGP_LABEL_CB, lcb);
+ return;
+ }
+ }
+
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /*
+ * Fast path: we filled the request from local pool (or
+ * this is a duplicate request that we filled already).
+ * Enqueue response work item with new label.
+ */
+ struct lp_cbq_item *q;
+
+ q = XCALLOC(MTYPE_BGP_LABEL_CBQ, sizeof(struct lp_cbq_item));
+
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = true;
+
+ work_queue_add(lp->callback_q, q);
+
+ return;
+ }
+
+ if (requested)
+ return;
+
+ if (debug)
+ zlog_debug("%s: slow path. lcb=%p label=%u",
+ __func__, lcb, lcb->label);
+
+ /*
+ * Slow path: we are out of labels in the local pool,
+ * so remember the request and also get another chunk from
+ * the label manager.
+ *
+ * We track number of outstanding label requests: don't
+ * need to get a chunk for each one.
+ */
+
+ struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+ sizeof(struct lp_fifo));
+
+ lf->lcb = *lcb;
+ LABEL_FIFO_ADD(lp->requests, lf);
+
+ if (LABEL_FIFO_COUNT(lp->requests) > lp->pending_count) {
+ if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) {
+ lp->pending_count += LP_CHUNK_SIZE;
+ return;
+ }
+ }
+}
+
+void bgp_lp_release(
+ int type,
+ void *labelid,
+ mpls_label_t label)
+{
+ struct lp_lcb *lcb;
+
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ if (label == lcb->label && type == lcb->type) {
+ uintptr_t lbl = label;
+
+ /* no longer in use */
+ skiplist_delete(lp->inuse, (void *)lbl, NULL);
+
+ /* no longer requested */
+ skiplist_delete(lp->ledger, labelid, NULL);
+ }
+ }
+}
+
+/*
+ * zebra response giving us a chunk of labels
+ */
+void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
+{
+ struct lp_chunk *chunk;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+ struct lp_fifo *lf;
+
+ if (last < first) {
+ zlog_err("%s: zebra label chunk invalid: first=%u, last=%u",
+ __func__, first, last);
+ return;
+ }
+
+ chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk));
+
+ chunk->first = first;
+ chunk->last = last;
+
+ listnode_add(lp->chunks, chunk);
+
+ lp->pending_count -= (last - first + 1);
+
+ if (debug) {
+ zlog_debug("%s: %u pending requests", __func__,
+ LABEL_FIFO_COUNT(lp->requests));
+ }
+
+ while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
+
+ struct lp_lcb *lcb;
+ void *labelid = lf->lcb.labelid;
+
+ if (skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ /* request no longer in effect */
+
+ if (debug) {
+ zlog_debug("%s: labelid %p: request no longer in effect",
+ __func__, labelid);
+ }
+ goto finishedrequest;
+ }
+
+ /* have LCB */
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /* request already has a label */
+ if (debug) {
+ zlog_debug("%s: labelid %p: request already has a label: %u=0x%x, lcb=%p",
+ __func__, labelid,
+ lcb->label, lcb->label, lcb);
+ }
+ goto finishedrequest;
+ }
+
+ lcb->label = get_label_from_pool(lcb->labelid);
+
+ if (lcb->label == MPLS_LABEL_NONE) {
+ /*
+ * Out of labels in local pool, await next chunk
+ */
+ if (debug) {
+ zlog_debug("%s: out of labels, await more",
+ __func__);
+ }
+ break;
+ }
+
+ /*
+ * we filled the request from local pool.
+ * Enqueue response work item with new label.
+ */
+ struct lp_cbq_item *q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+ sizeof(struct lp_cbq_item));
+
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = true;
+
+ if (debug)
+ zlog_debug("%s: assigning label %u to labelid %p",
+ __func__, q->label, q->labelid);
+
+ work_queue_add(lp->callback_q, q);
+
+finishedrequest:
+ LABEL_FIFO_DEL(lp->requests, lf);
+ XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+ }
+}
+
+/*
+ * continue using allocated labels until zebra returns
+ */
+void bgp_lp_event_zebra_down(void)
+{
+ /* rats. */
+}
+
+/*
+ * Inform owners of previously-allocated labels that their labels
+ * are not valid. Request chunk from zebra large enough to satisfy
+ * previously-allocated labels plus any outstanding requests.
+ */
+void bgp_lp_event_zebra_up(void)
+{
+ int labels_needed;
+ int chunks_needed;
+ void *labelid;
+ struct lp_lcb *lcb;
+
+ /*
+ * Get label chunk allocation request dispatched to zebra
+ */
+ labels_needed = LABEL_FIFO_COUNT(lp->requests) +
+ skiplist_count(lp->inuse);
+
+ /* round up */
+ chunks_needed = (labels_needed / LP_CHUNK_SIZE) + 1;
+ labels_needed = chunks_needed * LP_CHUNK_SIZE;
+
+ zclient_send_get_label_chunk(zclient, 0, labels_needed);
+ lp->pending_count = labels_needed;
+
+ /*
+ * Invalidate current list of chunks
+ */
+ list_delete_all_node(lp->chunks);
+
+ /*
+ * Invalidate any existing labels and requeue them as requests
+ */
+ while (!skiplist_first(lp->inuse, NULL, &labelid)) {
+
+ /*
+ * Get LCB
+ */
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /*
+ * invalidate
+ */
+ struct lp_cbq_item *q;
+
+ q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+ sizeof(struct lp_cbq_item));
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = false;
+ work_queue_add(lp->callback_q, q);
+
+ lcb->label = MPLS_LABEL_NONE;
+ }
+
+ /*
+ * request queue
+ */
+ struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+ sizeof(struct lp_fifo));
+
+ lf->lcb = *lcb;
+ LABEL_FIFO_ADD(lp->requests, lf);
+ }
+
+ skiplist_delete_first(lp->inuse);
+ }
+}
--- /dev/null
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program 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 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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
+ */
+
+#ifndef _FRR_BGP_LABELPOOL_H
+#define _FRR_BGP_LABELPOOL_H
+
+#include <zebra.h>
+
+#include "mpls.h"
+
+/*
+ * Types used in bgp_lp_get for debug tracking; add more as needed
+ */
+#define LP_TYPE_VRF 0x00000001
+
+struct labelpool {
+ struct skiplist *ledger; /* all requests */
+ struct skiplist *inuse; /* individual labels */
+ struct list *chunks; /* granted by zebra */
+ struct lp_fifo *requests; /* blocked on zebra */
+ struct work_queue *callback_q;
+ uint32_t pending_count; /* requested from zebra */
+};
+
+extern void bgp_lp_init(struct thread_master *master, struct labelpool *pool);
+extern void bgp_lp_finish(void);
+extern void bgp_lp_get(int type, void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated));
+extern void bgp_lp_release(int type, void *labelid, mpls_label_t label);
+extern void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last);
+extern void bgp_lp_event_zebra_down(void);
+extern void bgp_lp_event_zebra_up(void);
+
+#endif /* _FRR_BGP_LABELPOOL_H */
*/
new_mpath = listgetdata(mp_node);
list_delete_node(mp_list, mp_node);
+ assert(new_mpath);
+ assert(prev_mpath);
if ((mpath_count < maxpaths) && (new_mpath != new_best)
&& bgp_info_nexthop_cmp(prev_mpath, new_mpath)) {
if (new_mpath == next_mpath)
bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
}
+int vpn_leak_label_callback(
+ mpls_label_t label,
+ void *labelid,
+ bool allocated)
+{
+ struct vpn_policy *vp = (struct vpn_policy *)labelid;
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+ if (debug)
+ zlog_debug("%s: label=%u, allocated=%d",
+ __func__, label, allocated);
+
+ if (!allocated) {
+ /*
+ * previously-allocated label is now invalid
+ */
+ if (CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
+ (vp->tovpn_label != MPLS_LABEL_NONE)) {
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ vp->tovpn_label = MPLS_LABEL_NONE;
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ }
+ return 0;
+ }
+
+ /*
+ * New label allocation
+ */
+ if (!CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ /*
+ * not currently configured for auto label, reject allocation
+ */
+ return -1;
+ }
+
+ if (vp->tovpn_label != MPLS_LABEL_NONE) {
+ if (label == vp->tovpn_label) {
+ /* already have same label, accept but do nothing */
+ return 0;
+ }
+ /* Shouldn't happen: different label allocation */
+ zlog_err("%s: %s had label %u but got new assignment %u",
+ __func__, vp->bgp->name_pretty, vp->tovpn_label, label);
+ /* use new one */
+ }
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ vp->tovpn_label = label;
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+
+ return 0;
+}
+
static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
{
int i;
return 0;
}
+static bool labels_same(struct bgp_info *bi, mpls_label_t *label, uint32_t n)
+{
+ uint32_t i;
+
+ if (!bi->extra) {
+ if (!n)
+ return true;
+ else
+ return false;
+ }
+
+ if (n != bi->extra->num_labels)
+ return false;
+
+ for (i = 0; i < n; ++i) {
+ if (label[i] != bi->extra->label[i])
+ return false;
+ }
+ return true;
+}
+
+/*
+ * make encoded route labels match specified encoded label set
+ */
+static void setlabels(
+ struct bgp_info *bi,
+ mpls_label_t *label, /* array of labels */
+ uint32_t num_labels)
+{
+ if (num_labels)
+ assert(label);
+ assert(num_labels <= BGP_MAX_LABELS);
+
+ if (!num_labels) {
+ if (bi->extra)
+ bi->extra->num_labels = 0;
+ return;
+ }
+
+ struct bgp_info_extra *extra = bgp_info_extra_get(bi);
+ uint32_t i;
+
+ for (i = 0; i < num_labels; ++i) {
+ extra->label[i] = label[i];
+ if (!bgp_is_valid_label(&label[i])) {
+ bgp_set_valid_label(&extra->label[i]);
+ }
+ }
+ extra->num_labels = num_labels;
+}
+
/*
* returns pointer to new bgp_info upon success
*/
safi_t safi,
struct bgp_info *source_bi,
mpls_label_t *label,
- int num_labels,
+ uint32_t num_labels,
void *parent,
struct bgp *bgp_orig,
struct prefix *nexthop_orig,
}
if (bi) {
+ bool labelssame = labels_same(bi, label, num_labels);
+
if (attrhash_cmp(bi->attr, new_attr)
+ && labelssame
&& !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) {
bgp_attr_unintern(&new_attr);
bi->attr = new_attr;
bi->uptime = bgp_clock();
+ /*
+ * rewrite labels
+ */
+ if (!labelssame)
+ setlabels(bi, label, num_labels);
+
if (nexthop_self_flag)
bgp_info_set_flag(bn, bi, BGP_INFO_ANNC_NH_SELF);
if (nexthop_self_flag)
bgp_info_set_flag(bn, new, BGP_INFO_ANNC_NH_SELF);
+ if (num_labels)
+ setlabels(new, label, num_labels);
+
bgp_info_extra_get(new);
- if (label) {
- int i;
-
- for (i = 0; i < num_labels; ++i) {
- new->extra->label[i] = label[i];
- if (!bgp_is_valid_label(&label[i])) {
- if (debug) {
- zlog_debug(
- "%s: %s: marking label %d valid",
- __func__, buf_prefix, i);
- }
- bgp_set_valid_label(&new->extra->label[i]);
- }
- }
- new->extra->num_labels = num_labels;
- }
new->extra->parent = parent;
if (bgp_orig)
const char *debugmsg;
struct prefix nexthop_orig;
mpls_label_t *pLabels = NULL;
- int num_labels = 0;
+ uint32_t num_labels = 0;
int nexthop_self_flag = 1;
+ struct bgp_info *bi_ultimate = NULL;
+ int origin_local = 0;
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
/*
* ensure labels are copied
+ *
+ * However, there is a special case: if the route originated in
+ * another local VRF (as opposed to arriving via VPN), then the
+ * nexthop is reached by hairpinning through this router (me)
+ * using IP forwarding only (no LSP). Therefore, the route
+ * imported to the VRF should not have labels attached. Note
+ * that nexthop tracking is also involved: eliminating the
+ * labels for these routes enables the non-labeled nexthops
+ * from the originating VRF to be considered valid for this route.
*/
- if (info_vpn->extra && info_vpn->extra->num_labels) {
+
+ /* work back to original route */
+ for (bi_ultimate = info_vpn;
+ bi_ultimate->extra && bi_ultimate->extra->parent;
+ bi_ultimate = bi_ultimate->extra->parent)
+ ;
+
+ /* if original route was unicast, then it did not arrive over vpn */
+ if (bi_ultimate->net) {
+ struct bgp_table *table;
+
+ table = bgp_node_table(bi_ultimate->net);
+ if (table && (table->safi == SAFI_UNICAST))
+ origin_local = 1;
+ }
+
+ /* copy labels */
+ if (!origin_local && info_vpn->extra && info_vpn->extra->num_labels) {
num_labels = info_vpn->extra->num_labels;
if (num_labels > BGP_MAX_LABELS)
num_labels = BGP_MAX_LABELS;
pLabels = info_vpn->extra->label;
}
+
if (debug) {
char buf_prefix[PREFIX_STRLEN];
prefix2str(p, buf_prefix, sizeof(buf_prefix));
extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi);
extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi);
+extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc);
static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
const char **pmsg)
*pmsg = "rd not defined";
return 0;
}
+
+ /* Is there an "auto" export label that isn't allocated yet? */
+ if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
+ (bgp_vrf->vpn_policy[afi].tovpn_label == MPLS_LABEL_NONE)) {
+
+ if (pmsg)
+ *pmsg = "auto label not allocated";
+ return 0;
+ }
+
return 1;
}
uint8_t sub_code, uint8_t *data, size_t datalen)
{
struct stream *s;
- int length;
/* Lock I/O mutex to prevent other threads from pushing packets */
pthread_mutex_lock(&peer->io_mtx);
stream_write(s, data, datalen);
/* Set BGP packet length. */
- length = bgp_packet_set_size(s);
+ bgp_packet_set_size(s);
/* wipe output buffer */
stream_fifo_clean(peer->obuf);
bgp_notify.code = code;
bgp_notify.subcode = sub_code;
bgp_notify.data = NULL;
- bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE;
+ bgp_notify.length = datalen;
bgp_notify.raw_data = data;
peer->notify.code = bgp_notify.code;
peer->notify.subcode = bgp_notify.subcode;
- if (bgp_notify.length) {
+ if (bgp_notify.length && data) {
bgp_notify.data =
XMALLOC(MTYPE_TMP, bgp_notify.length * 3);
for (i = 0; i < bgp_notify.length; i++)
if (debug) {
bgp_info_path_with_addpath_rx_str(new_select,
path_buf);
- zlog_debug("%s: %s is the bestpath from AS %d",
+ zlog_debug("%s: %s is the bestpath from AS %u",
pfx_buf, path_buf,
aspath_get_first_as(
new_select->attr->aspath));
} else
vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
} else if (safi == SAFI_FLOWSPEC) {
- /* already done */
- /* IPv4 Next Hop */
+ if (attr->nexthop.s_addr != 0) {
+ if (json_paths) {
+ json_nexthop_global = json_object_new_object();
+ json_object_string_add(
+ json_nexthop_global, "ip",
+ inet_ntoa(attr->nexthop));
+ json_object_string_add(json_nexthop_global,
+ "afi", "ipv4");
+ json_object_boolean_true_add(json_nexthop_global,
+ "used");
+ } else {
+ vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
+ }
+ }
} else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
if (json_paths) {
json_nexthop_global = json_object_new_object();
default:
vty_out(vty, "?");
}
+
+ char *str = esi2str(&(attr->evpn_overlay.eth_s_id));
+
+ vty_out(vty, "%s", str);
+ XFREE(MTYPE_TMP, str);
+
+ if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) {
+ vty_out(vty, "/%s",
+ inet_ntoa(attr->evpn_overlay.gw_ip.ipv4));
+ } else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p)) {
+ vty_out(vty, "/%s",
+ inet_ntop(AF_INET6,
+ &(attr->evpn_overlay.gw_ip.ipv6), buf,
+ BUFSIZ));
+ }
+ if (attr->ecommunity) {
+ char *mac = NULL;
+ struct ecommunity_val *routermac = ecommunity_lookup(
+ attr->ecommunity, ECOMMUNITY_ENCODE_EVPN,
+ ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC);
+ if (routermac)
+ mac = ecom_mac2str((char *)routermac->val);
+ if (mac) {
+ vty_out(vty, "/%s", (char *)mac);
+ XFREE(MTYPE_TMP, mac);
+ }
+ }
+ vty_out(vty, "\n");
}
- struct eth_segment_id *id = &(attr->evpn_overlay.eth_s_id);
- char *str = esi2str(id);
- vty_out(vty, "%s", str);
- XFREE(MTYPE_TMP, str);
- if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) {
- vty_out(vty, "/%s", inet_ntoa(attr->evpn_overlay.gw_ip.ipv4));
- } else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p)) {
- vty_out(vty, "/%s",
- inet_ntop(AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6),
- buf, BUFSIZ));
- }
- if (attr->ecommunity) {
- char *mac = NULL;
- struct ecommunity_val *routermac = ecommunity_lookup(
- attr->ecommunity, ECOMMUNITY_ENCODE_EVPN,
- ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC);
- if (routermac)
- mac = ecom_mac2str((char *)routermac->val);
- if (mac) {
- vty_out(vty, "/%s", (char *)mac);
- XFREE(MTYPE_TMP, mac);
- }
- }
- vty_out(vty, "\n");
}
/* dampening route */
DEFPY (af_label_vpn_export,
af_label_vpn_export_cmd,
- "[no] label vpn export (0-1048575)$label_val",
+ "[no] label vpn export <(0-1048575)$label_val|auto$label_auto>",
NO_STR
"label value for VRF\n"
"Between current address-family and vpn\n"
"For routes leaked from current address-family to vpn\n"
- "Label Value <0-1048575>\n")
+ "Label Value <0-1048575>\n"
+ "Automatically assign a label\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
mpls_label_t label = MPLS_LABEL_NONE;
if (argv_find(argv, argc, "no", &idx))
yes = 0;
- if (yes)
- label = label_val; /* rely on parser to force unsigned */
+ if (yes) {
+ if (!label_auto)
+ label = label_val; /* parser should force unsigned */
+ }
ret = vpn_policy_getafi(vty, doafi);
if (ret != CMD_SUCCESS)
if (!doafi[afi])
continue;
+ if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO))
+
+ continue; /* no change */
+
/*
* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
*/
vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
bgp_get_default(), bgp);
+ if (!label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ if (bgp->vpn_policy[afi].tovpn_label !=
+ MPLS_LABEL_NONE) {
+
+ /*
+ * label has previously been automatically
+ * assigned by labelpool: release it
+ *
+ * NB if tovpn_label == MPLS_LABEL_NONE it
+ * means the automatic assignment is in flight
+ * and therefore the labelpool callback must
+ * detect that the auto label is not needed.
+ */
+
+ bgp_lp_release(LP_TYPE_VRF,
+ &bgp->vpn_policy[afi],
+ bgp->vpn_policy[afi].tovpn_label);
+ }
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO);
+ }
+
bgp->vpn_policy[afi].tovpn_label = label;
+ if (label_auto) {
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO);
+ bgp_lp_get(LP_TYPE_VRF, &bgp->vpn_policy[afi],
+ vpn_leak_label_callback);
+ }
/* post-change: re-export vpn routes */
vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
{
int indent = 2;
- if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) {
- vty_out(vty, "%*slabel vpn export %u\n", indent, "",
- bgp->vpn_policy[afi].tovpn_label);
+ if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ vty_out(vty, "%*slabel vpn export %s\n", indent, "", "auto");
+
+ } else {
+ if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) {
+ vty_out(vty, "%*slabel vpn export %u\n", indent, "",
+ bgp->vpn_policy[afi].tovpn_label);
+ }
}
if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_RD_SET)) {
"Specify community to accept\n"
COMMUNITY_VAL_STR)
{
- int delete_all = 0;
-
char *cl_name_or_number = NULL;
int direct = 0;
int style = COMMUNITY_LIST_STANDARD;
char *str = argv_concat(argv, argc, idx);
int ret = community_list_unset(bgp_clist, cl_name_or_number, str,
- direct, style, delete_all);
+ direct, style);
XFREE(MTYPE_TMP, str);
"Specify community to accept\n"
COMMUNITY_VAL_STR)
{
- int delete_all = 0;
-
char *cl_name_or_number = NULL;
int direct = 0;
int style = COMMUNITY_LIST_EXPANDED;
char *str = argv_concat(argv, argc, idx);
int ret = community_list_unset(bgp_clist, cl_name_or_number, str,
- direct, style, delete_all);
+ direct, style);
XFREE(MTYPE_TMP, str);
"Specify community to accept\n"
EXTCOMMUNITY_VAL_STR)
{
- int deleteall = 0;
-
int style = EXTCOMMUNITY_LIST_STANDARD;
int direct = 0;
char *cl_number_or_name = NULL;
char *str = argv_concat(argv, argc, idx);
int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str,
- direct, style, deleteall);
+ direct, style);
XFREE(MTYPE_TMP, str);
"Specify community to accept\n"
"An ordered list as a regular-expression\n")
{
- int deleteall = 0;
-
int style = EXTCOMMUNITY_LIST_EXPANDED;
int direct = 0;
char *cl_number_or_name = NULL;
char *str = argv_concat(argv, argc, idx);
int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str,
- direct, style, deleteall);
+ direct, style);
XFREE(MTYPE_TMP, str);
#endif
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_labelpool.h"
/* All information about zebra. */
struct zclient *zclient = NULL;
/* Send the client registration */
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ /* tell label pool that zebra is connected */
+ bgp_lp_event_zebra_up();
+
/* TODO - What if we have peers and networks configured, do we have to
* kick-start them?
*/
}
}
+static void bgp_zebra_process_label_chunk(
+ int cmd,
+ struct zclient *zclient,
+ zebra_size_t length,
+ vrf_id_t vrf_id)
+{
+ struct stream *s = NULL;
+ uint8_t response_keep;
+ uint32_t first;
+ uint32_t last;
+
+ s = zclient->ibuf;
+ STREAM_GETC(s, response_keep);
+ STREAM_GETL(s, first);
+ STREAM_GETL(s, last);
+
+ if (first > last ||
+ first < MPLS_LABEL_UNRESERVED_MIN ||
+ last > MPLS_LABEL_UNRESERVED_MAX) {
+
+ zlog_err("%s: Invalid Label chunk: %u - %u",
+ __func__, first, last);
+ return;
+ }
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ zlog_debug("Label Chunk assign: %u - %u (%u) ",
+ first, last, response_keep);
+ }
+
+ bgp_lp_event_chunk(response_keep, first, last);
+
+stream_failure: /* for STREAM_GETX */
+ return;
+}
+
extern struct zebra_privs_t bgpd_privs;
void bgp_zebra_init(struct thread_master *master)
zclient->local_l3vni_del = bgp_zebra_process_local_l3vni;
zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix;
zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix;
+ zclient->label_chunk = bgp_zebra_process_label_chunk;
}
void bgp_zebra_destroy(void)
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_flowspec.h"
-
+#include "bgpd/bgp_labelpool.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_QOBJ_TYPE(bgp_master)
#endif /* ENABLE_BGP_VNC */
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ bgp->vpn_policy[afi].bgp = bgp;
+ bgp->vpn_policy[afi].afi = afi;
bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent =
MPLS_LABEL_NONE;
/* Enable multiple instances by default. */
bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE);
+ /* mpls label dynamic allocation pool */
+ bgp_lp_init(bm->master, &bm->labelpool);
+
QOBJ_REG(bm, bgp_master);
}
if (bm->t_rmap_update)
BGP_TIMER_OFF(bm->t_rmap_update);
+
+ bgp_lp_finish();
}
#include "bgp_memory.h"
#include "bitfield.h"
#include "vxlan.h"
+#include "bgp_labelpool.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
/* Id space for automatic RD derivation for an EVI/VRF */
bitfield_t rd_idspace;
+ /* dynamic mpls label allocation pool */
+ struct labelpool labelpool;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp_master)
BGP_VPN_POLICY_DIR_MAX = 2
} vpn_policy_direction_t;
+struct vpn_policy {
+ struct bgp *bgp; /* parent */
+ afi_t afi;
+ struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX];
+ struct ecommunity *import_redirect_rtlist;
+ char *rmap_name[BGP_VPN_POLICY_DIR_MAX];
+ struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX];
+
+ /* should be mpls_label_t? */
+ uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */
+ uint32_t tovpn_zebra_vrf_label_last_sent;
+ struct prefix_rd tovpn_rd;
+ struct prefix tovpn_nexthop; /* unset => set to 0 */
+ uint32_t flags;
+#define BGP_VPN_POLICY_TOVPN_LABEL_AUTO (1 << 0)
+#define BGP_VPN_POLICY_TOVPN_RD_SET (1 << 1)
+#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET (1 << 2)
+};
+
/*
* Type of 'struct bgp'.
* - Default: The default instance
/* route map for advertise ipv4/ipv6 unicast (type-5 routes) */
struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX];
- /* vpn-policy */
- struct {
- struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX];
- struct ecommunity *import_redirect_rtlist;
- char *rmap_name[BGP_VPN_POLICY_DIR_MAX];
- struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX];
-
- /* should be mpls_label_t? */
- uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */
- uint32_t tovpn_zebra_vrf_label_last_sent;
- struct prefix_rd tovpn_rd;
- struct prefix tovpn_nexthop; /* unset => set to 0 */
- uint32_t flags;
-#define BGP_VPN_POLICY_TOVPN_RD_SET 0x00000004
-#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET 0x00000008
- } vpn_policy[AFI_MAX];
+ struct vpn_policy vpn_policy[AFI_MAX];
QOBJ_FIELDS
};
rfg = XCALLOC(MTYPE_RFAPI_GROUP_CFG,
sizeof(struct rfapi_nve_group_cfg));
- if (rfg) {
- rfg->type = type;
- rfg->name = strdup(name);
- /* add to tail of list */
- listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg);
- }
+ rfg->type = type;
+ rfg->name = strdup(name);
+ /* add to tail of list */
+ listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg);
rfg->label = MPLS_LABEL_NONE;
+
QOBJ_REG(rfg, rfapi_nve_group_cfg);
return rfg;
int count = 0;
int is_l2 = (rn->p.family == AF_ETHERNET);
- if (rfapiRibFTDFilterRecentPrefix(
- (struct rfapi_descriptor *)(rfd_rib_node->table->info), rn,
- pfx_target_original)) {
- return 0;
+ if (rfd_rib_node && rfd_rib_node->table && rfd_rib_node->table->info) {
+ struct rfapi_descriptor *rfd;
+
+ rfd = (struct rfapi_descriptor *)(rfd_rib_node->table->info);
+
+ if (rfapiRibFTDFilterRecentPrefix(
+ rfd, rn, pfx_target_original))
+
+ return 0;
}
seen_nexthops =
uint32_t lifetime;
struct rfapi_withdraw *wcb;
- if
- CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)
- {
- /*
- * Already on the path to being withdrawn,
- * should already have a timer set up to
- * delete it.
- */
- vnc_zlog_debug_verbose(
- "%s: already being withdrawn, do nothing",
- __func__);
- return;
- }
+ if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) {
+ /*
+ * Already on the path to being withdrawn,
+ * should already have a timer set up to
+ * delete it.
+ */
+ vnc_zlog_debug_verbose(
+ "%s: already being withdrawn, do nothing", __func__);
+ return;
+ }
rfapiGetVncLifetime(bi->attr, &lifetime);
vnc_zlog_debug_verbose("%s: VNC lifetime is %u", __func__, lifetime);
figures/ospf_api_architecture.png \
figures/ospf_api_msghdr.png \
figures/ospf_api_msgs1.png \
- figures/ospf_api_msgs2.png
+ figures/ospf_api_msgs2.png \
+ extra/frrlexer.py
div.body {
max-width: none;
}
+
+pre {
+ background-color: #e2e2e2;
+}
to the documentation here for instructions on how to install a free version of
docker: https://www.docker.com/community-edition
+Pre-built packages and docker images
+------------------------------------
+
+The master branch of https://github.com/frrouting/frr.git has a
+continuous delivery of docker images to docker hub at:
+https://hub.docker.com/r/ajones17/frr/. These images have the frr packages
+in /pkgs/apk and have the frr package pre-installed. To copy Alpine
+packages out of these images:
+
+::
+
+ id=`docker create ajones17/frr:latest`
+ docker cp ${id}:/pkgs _some_directory_
+ docker rm $id
+
+To run the frr daemons (see below for how to configure them):
+
+::
+
+ docker run -it --rm --name frr ajones17/frr:latest
+ docker exec -it frr /bin/sh
+
Work with sources
-----------------
::
- docker run -it --rm frr:latest /bin/sh
+ docker run -it --rm --name frr frr:latest
+
+In the default configuration, none of the frr daemons will be running.
+To configure the daemons, exec into the container and edit the configuration
+files or mount a volume with configuration files into the container on
+startup. To configure by hand:
+
+::
+
+ docker exec -it frr /bin/sh
+ vi /etc/frr/daemons
+ vi /etc/frr/daemons.conf
+ cp /etc/frr/zebra.conf.sample /etc/frr/zebra.conf
+ vi /etc/frr/zebra.conf
+ /etc/init.d/frr start
+
+Or, to configure the daemons using /etc/frr from a host volume, put the
+config files in, say, ./docker/etc and bind mount that into the
+container:
+
+::
-Currently, we only package the raw daemons and example files, so, you'll
-need to run the daemons by hand (or, better, orchestrate in the Dockerfile).
+ docker run -it --rm -v `pwd`/docker/etc:/etc/frr frr:latest
-We can also build directly from docker-compose, with a docker-compose.yml file
-like this one:
+We can also build the base image directly from docker-compose, with a
+docker-compose.yml file like this one:
::
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
-c "FRR FRRouting suite" -d /var/run/frr frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make SPHINXBUILD=sphinx-build2.7 install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /etc/frr/*.conf
Install daemon config file
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc.
Enable the daemons as required by changing the value to ``yes``
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and set the following values (ignore the other
settings)
sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
Add init.d startup files
-~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chkconfig --add frr
Enable frr daemon at startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chkconfig frr on
Start FRR manually (or reboot)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
-c "FRR FRRouting suite" -d /var/run/frr frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /etc/frr/*.conf
Install daemon config file
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc.
Enable the daemons as required by changing the value to ``yes``
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the
following content:
sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
Install frr Service and redhat init files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr
Register the systemd files
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo systemctl preset frr.service
Enable required frr at startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo systemctl enable frr
Reboot or start FRR manually
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
system
Troubleshooting
-~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^
**Local state directory**
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
---------------
Shared library error
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
If you try and start any of the frrouting daemons you may see the below
error due to the frrouting shared library directory not being found:
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
-c "FRR FRRouting suite" -d /var/run/frr frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /etc/frr/*.conf
Install daemon config file
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc.
Enable the daemons as required by changing the value to ``yes``
Enable IP & IPv6 forwarding (and MPLS)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the
following content: (Please make sure to list all interfaces with
sudo modprobe mpls-router mpls-iptunnel
Install frr Service and redhat init files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr
Enable required frr at startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo systemctl enable frr
Reboot or start FRR manually
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /usr/local/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /usr/local/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /usr/local/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
isisd nhrpd ospfd ospf6d pimd ripd ripngd
Enable the serivce
-~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^
- service frr enable
Start the service
-~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^
- service frr start
---------------------------------------------
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
-d /nonexistent -s /sbin/nologin frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example)
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /usr/pkg/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
system
Install rc.d init files
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
chmod 555 /etc/rc.d/*.sh
Enable FRR processes
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
(Enable the required processes only)
---------------------------------------------
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
-d /nonexistent -s /sbin/nologin frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example)
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chmod 640 /usr/pkg/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
system
Install rc.d init files
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
chmod 555 /etc/rc.d/*.sh
Enable FRR processes
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
(Enable the required processes only)
use without MPLS
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
sudo gmake install
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
-d /nonexistent -s /sbin/nologin _frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example)
doas gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
doas chmod 640 /etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/rc.conf``:
**Reboot** to apply the config to the system
Enable MPLS Forwarding
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
To enable MPLS forwarding on a given interface, use the following
command:
inet 10.0.1.1 255.255.255.0 mpls
Install rc.d init files
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
(create them in /etc/rc.d - no example are included at this time with
FRR source)
rc_cmd $1
Enable FRR processes
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
(Enable the required processes only)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
system
Install the init.d service
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
isisd=yes
Start the init.d service
-~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^
- /etc/init.d/frr start
- use ``/etc/init.d/frr status`` to check its status.
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
net.ipv6.conf.all.forwarding=1
**Reboot** or use ``sysctl -p`` to apply the same config to the running
-system ### Install the init.d service
+system
+
+Install the init.d service
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
isisd=yes
Start the init.d service
-~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^
- /etc/init.d/frr start
- use ``/etc/init.d/frr status`` to check its status.
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IPv4 & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
net.ipv6.conf.all.forwarding=1
Enable MPLS Forwarding (with Linux Kernel >= 4.5)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a
line equal to ``net.mpls.conf.eth0.input`` or each interface used with
net.mpls.platform_labels=100000
Add MPLS kernel modules
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to ``/etc/modules-load.d/modules.conf``:
system
Install the systemd service (if rebooted from last step, change directory back to frr directory)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
isisd=yes
Enable the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl enable frr
Start the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl start frr
- use ``systemctl status frr`` to check its status.
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IPv4 & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
net.ipv6.conf.all.forwarding=1
Enable MPLS Forwarding (with Linux Kernel >= 4.5)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a
line equal to ``net.mpls.conf.eth0.input`` or each interface used with
net.mpls.platform_labels=100000
Add MPLS kernel modules
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to ``/etc/modules-load.d/modules.conf``:
system
Install the systemd service (if rebooted from last step, change directory back to frr directory)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
isisd=yes
Enable the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl enable frr
Start the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl start frr
- use ``systemctl status frr`` to check its status.
+************
Building FRR
-=========================
+************
.. toctree::
:maxdepth: 2
import sys
import os
import re
+import pygments
+from sphinx.highlighting import lexers
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# General information about the project.
project = u'FRR'
copyright = u'2017, FRR'
-author = u'FRR'
+author = u'FRR authors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# Extract values from codebase for substitution into docs.
# -----------------------------------------------------------------------------
-# Various installation prefixes. Reasonable defaults are set where possible.
-# Values are overridden by logic below.
+# Various installation prefixes. Values are extracted from config.status.
+# Reasonable defaults are set in case that file does not exist.
replace_vars = {
- 'AUTHORS': 'Kunihiro Ishiguro, et al.',
+ 'AUTHORS': author,
'COPYRIGHT_YEAR': '1999-2005',
- 'COPYRIGHT_STR': None,
+ 'COPYRIGHT_STR': 'Copyright (c) 1999-2005',
'PACKAGE_NAME': project.lower(),
'PACKAGE_TARNAME': project.lower(),
- 'PACKAGE_STRING': None,
+ 'PACKAGE_STRING': project.lower() + ' latest',
'PACKAGE_URL': 'https://frrouting.org/',
- 'PACKAGE_VERSION': None,
- 'INSTALL_PREFIX_ETC': None,
- 'INSTALL_PREFIX_SBIN': None,
- 'INSTALL_PREFIX_STATE': None,
- 'INSTALL_PREFIX_MODULES': None,
- 'INSTALL_USER': None,
- 'INSTALL_GROUP': None,
- 'INSTALL_VTY_GROUP': None,
+ 'PACKAGE_VERSION': 'latest',
+ 'INSTALL_PREFIX_ETC': '/etc/frr',
+ 'INSTALL_PREFIX_SBIN': '/usr/lib/frr',
+ 'INSTALL_PREFIX_STATE': '/var/run/frr',
+ 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules',
+ 'INSTALL_USER': 'frr',
+ 'INSTALL_GROUP': 'frr',
+ 'INSTALL_VTY_GROUP': 'frrvty',
+ 'GROUP': 'frr',
+ 'USER': 'frr',
}
# extract version information, installation location, other stuff we need to
# use when building final documents
val = re.compile('^S\["([^"]+)"\]="(.*)"$')
-with open('../../config.status', 'r') as cfgstatus:
- for ln in cfgstatus.readlines():
- m = val.match(ln)
- if not m or m.group(1) not in replace_vars.keys(): continue
- replace_vars[m.group(1)] = m.group(2)
+try:
+ with open('../../config.status', 'r') as cfgstatus:
+ for ln in cfgstatus.readlines():
+ m = val.match(ln)
+ if not m or m.group(1) not in replace_vars.keys(): continue
+ replace_vars[m.group(1)] = m.group(2)
+except IOError:
+ # if config.status doesn't exist, just ignore it
+ pass
# manually fill out some of these we can't get from config.status
replace_vars['COPYRIGHT_STR'] = "Copyright (c)"
app.add_object_type('clicmd', 'clicmd')
# css overrides for HTML theme
app.add_stylesheet('overrides.css')
+ # load Pygments lexer for FRR config syntax
+ #
+ # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we
+ # do it manually since not all of our supported build platforms have 2.2
+ # yet.
+ #
+ # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer")
+ custom_namespace = {}
+ exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace)
+ lexers['frr'] = custom_namespace['FRRLexer']()
Example:
.. code-block:: c
- :caption: mydaemon.h
+ :caption: mydaemon.h
- #include "hook.h"
- DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info))
+ #include "hook.h"
+ DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info))
.. code-block:: c
- :caption: mydaemon.c
+ :caption: mydaemon.c
- #include "mydaemon.h"
- DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info))
- ...
- hook_call(some_update_event, info);
+ #include "mydaemon.h"
+ DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info))
+ ...
+ hook_call(some_update_event, info);
.. code-block:: c
- :caption: mymodule.c
+ :caption: mymodule.c
- #include "mydaemon.h"
- static int event_handler(struct eventinfo *info);
- ...
- hook_register(some_update_event, event_handler);
+ #include "mydaemon.h"
+ static int event_handler(struct eventinfo *info);
+ ...
+ hook_register(some_update_event, event_handler);
Do not use parameter names starting with "hook", these can collide with
names used by the hook code itself.
-libfrr library facilities
-=========================
+***************************
+Library Facilities (libfrr)
+***************************
.. toctree::
:maxdepth: 2
FRR includes wrappers arround ``malloc()`` and ``free()`` that count the number
of objects currently allocated, for each of a defined ``MTYPE``.
-To this extent, there are `memory groups` and `memory types`. Each memory
+To this extent, there are *memory groups* and *memory types*. Each memory
type must belong to a memory group, this is used just to provide some basic
structure.
Example:
.. code-block:: c
- :caption: mydaemon.h
+ :caption: mydaemon.h
- DECLARE_MGROUP(MYDAEMON)
- DECLARE_MTYPE(MYNEIGHBOR)
+ DECLARE_MGROUP(MYDAEMON)
+ DECLARE_MTYPE(MYNEIGHBOR)
.. code-block:: c
- :caption: mydaemon.c
-
- DEFINE_MGROUP( MYDAEMON, "My daemon's memory")
- DEFINE_MTYPE( MYDAEMON, MYNEIGHBOR, "Neighbor entry")
- DEFINE_MTYPE_STATIC(MYDAEMON, MYNEIGHBORNAME, "Neighbor name")
-
- struct neigh *neighbor_new(const char *name)
- {
- struct neigh *n = XMALLOC(MYNEIGHBOR, sizeof(*n));
- n->name = XSTRDUP(MYNEIGHBORNAME, name);
- return n;
- }
-
- void neighbor_free(struct neigh *n)
- {
- XFREE(MYNEIGHBORNAME, n->name);
- XFREE(MYNEIGHBOR, n);
- }
+ :caption: mydaemon.c
+
+ DEFINE_MGROUP( MYDAEMON, "My daemon's memory")
+ DEFINE_MTYPE( MYDAEMON, MYNEIGHBOR, "Neighbor entry")
+ DEFINE_MTYPE_STATIC(MYDAEMON, MYNEIGHBORNAME, "Neighbor name")
+
+ struct neigh *neighbor_new(const char *name)
+ {
+ struct neigh *n = XMALLOC(MYNEIGHBOR, sizeof(*n));
+ n->name = XSTRDUP(MYNEIGHBORNAME, name);
+ return n;
+ }
+
+ void neighbor_free(struct neigh *n)
+ {
+ XFREE(MYNEIGHBORNAME, n->name);
+ XFREE(MYNEIGHBOR, n);
+ }
Definition
------
Modules
-~~~~~~~
+^^^^^^^
The core design introduces an "nht" (next hop tracking) module in BGP
and "rnh" (recursive nexthop) module in Zebra. The "nht" module
zclient message format
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
ZEBRA_NEXTHOP_REGISTER and ZEBRA_NEXTHOP_UNREGISTER messages are
encoded in the following way:
BGP data structure
-~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^
Legend:
::
Zebra data structure
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
RNH table::
};
User interface changes
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
+ no shut all links to r4
Future work
-~~~~~~~~~~~
+^^^^^^^^^^^
- route-policy for next hop validation (e.g. ignore default route)
- damping for rapid next hop changes
----------------------
Concepts
-~~~~~~~~
+^^^^^^^^
Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various
information:
Information LSA s are received.
Overview
-~~~~~~~~
+^^^^^^^^
Following files where modified or added:
Figure 1: Overview of Segment Routing interaction
Module interactions
-~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^
To process incoming LSA, the code is based on the capability to call `hook()`
functions when LSA are inserted or delete to / from the LSDB and the
-------------
Linux Kernel
-~~~~~~~~~~~~
+^^^^^^^^^^^^
In order to use OSPF Segment Routing, you must setup MPLS data plane. Up to
know, only Linux Kernel version >= 4.5 is supported.
sysctl -w net.ipv4.conf.lo.rp_filter=0
OSPFd
-~~~~~
+^^^^^
Here it is a simple example of configuration to enable Segment Routing. Note
that `opaque capability` and `router information` must be set to activate
instrumentation and run through a series of tests to look for any results.
Testing your own code with this tool before submission is encouraged. You
can enable it by passing::
-
+
--enable-address-sanitizer
to ``configure``.
detecting data races. If you are working on or around multithreaded code,
extensive testing with this instrumtation enabled is *highly* recommended.
You can enable it by passing::
-
+
--enable-thread-sanitizer
to ``configure``.
Similar to AddressSanitizer, this tool provides runtime instrumentation for
detecting use of uninitialized heap memory. Testing your own code with this
tool before submission is encouraged. You can enable it by passing::
-
+
--enable-memory-sanitizer
to ``configure``.
reading was generated by Sphinx from RST source in
:file:`doc/developer/workflow.rst`. The documentation is structured as follows:
-+-----------------------+--------------------------------------------------------------+
-| Directory | Contents |
-+=======================+==============================================================+
-| :file:`doc/user` | User documentation; configuration guides; protocol overviews |
-+-----------------------+--------------------------------------------------------------+
-| :file:`doc/developer` | Developer's documentation; API specs; datastructures; |
-| | architecture overviews; project management procedure |
-+-----------------------+--------------------------------------------------------------+
-| :file:`doc/manpages` | Source for manpages |
-+-----------------------+--------------------------------------------------------------+
-| :file:`doc/figures` | Images and diagrams |
-+-----------------------+--------------------------------------------------------------+
-
-Each of these directories, with the exception of :file:`doc/figures`, contains
-a Sphinx-generated Makefile and configuration script :file:`conf.py` used to
-set various document parameters. The makefile can be used for a variety of
-targets; invoke `make help` in any of these directories for a listing of
-available output formats. For convenience, there is a top-level
-:file:`Makefile.am` that has targets for PDF and HTML documentation for both
-developer and user documentation, respectively. That makefile is also
-responsible for building manual pages packed with distribution builds.
++-----------------------+-------------------------------------------+
+| Directory | Contents |
++=======================+===========================================+
+| :file:`doc/user` | User documentation; configuration guides; |
+| | protocol overviews |
++-----------------------+-------------------------------------------+
+| :file:`doc/developer` | Developer's documentation; API specs; |
+| | datastructures; architecture overviews; |
+| | project management procedure |
++-----------------------+-------------------------------------------+
+| :file:`doc/manpages` | Source for manpages |
++-----------------------+-------------------------------------------+
+| :file:`doc/figures` | Images and diagrams |
++-----------------------+-------------------------------------------+
+| :file:`doc/extra` | Miscellaneous Sphinx extensions, scripts, |
+| | customizations, etc. |
++-----------------------+-------------------------------------------+
+
+Each of these directories, with the exception of :file:`doc/figures` and
+:file:`doc/extra`, contains a Sphinx-generated Makefile and configuration
+script :file:`conf.py` used to set various document parameters. The makefile
+can be used for a variety of targets; invoke `make help` in any of these
+directories for a listing of available output formats. For convenience, there
+is a top-level :file:`Makefile.am` that has targets for PDF and HTML
+documentation for both developer and user documentation, respectively. That
+makefile is also responsible for building manual pages packed with distribution
+builds.
Indent and styling should follow existing conventions:
being its own chapter. If you are adding a new protocol daemon, please create a
new chapter.
+FRR Specific Markup
+-------------------
+
+FRR has some customizations applied to the Sphinx markup that go a long way
+towards making documentation easier to use, write and maintain.
+
+CLI Commands
+^^^^^^^^^^^^
+
When documenting CLI please use a combination of the ``.. index::`` and
``.. clicmd::`` directives. For example, the command :clicmd:`show pony` would
be documented as follows:
This is very helpful for users who want to quickly remind themselves what a
particular command does.
+Configuration Snippets
+^^^^^^^^^^^^^^^^^^^^^^
+
+When putting blocks of example configuration please use the
+``.. code-block::`` directive and specify ``frr`` as the highlighting language,
+as in the following example. This will tell Sphinx to use a custom Pygments
+lexer to highlight FRR configuration syntax.
+
+.. code-block:: rest
+
+ .. code-block:: frr
+
+ !
+ ! Example configuration file.
+ !
+ log file /tmp/log.log
+ service integrated-vtysh-config
+ !
+ ip route 1.2.3.0/24 reject
+ ipv6 route de:ea:db:ee:ff::/64 reject
+ !
+
+
.. _GitHub: https://github.com/frrouting/frr
.. _GitHub issues: https://github.com/frrouting/frr/issues
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017 Vincent Bernat <bernat@luffy.cx>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from pygments.lexer import RegexLexer, bygroups
+from pygments.token import Text, Comment, Keyword
+from pygments.token import String, Number, Name
+
+
+class FRRLexer(RegexLexer):
+ name = "frr"
+ aliases = ["frr"]
+ tokens = {
+ 'root': [
+ (r'^[ \t]*!.*?\n', Comment.Singleline),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r'[a-f0-9]*:[a-f0-9]*:[a-f0-9:]*(:\d+\.\d+\.\d+\.\d+)?(/\d+)?',
+ Number), # IPv6
+ (r'\d+\.\d+\.\d+\.\d+(/\d+)?', Number), # IPv4
+ (r'^([ \t]*)(no[ \t]+)?([-\w]+)',
+ bygroups(Text, Keyword, Name.Function)),
+ (r'[ \t]+', Text),
+ (r'\n', Text),
+ (r'\d+', Number),
+ (r'\S+', Text),
+ ],
+ }
# General information about the project.
project = u'FRR'
copyright = u'2017, FRR'
-author = u'Kunihiro Ishiguro, et al.'
+author = u'FRR authors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# Extract values from codebase for substitution into docs.
# -----------------------------------------------------------------------------
-# Various installation prefixes. Reasonable defaults are set where possible.
-# Values are overridden by logic below.
+# Various installation prefixes. Values are extracted from config.status.
+# Reasonable defaults are set in case that file does not exist.
replace_vars = {
'AUTHORS': author,
'COPYRIGHT_YEAR': '1999-2005',
- 'COPYRIGHT_STR': None,
+ 'COPYRIGHT_STR': 'Copyright (c) 1999-2005',
'PACKAGE_NAME': project.lower(),
'PACKAGE_TARNAME': project.lower(),
- 'PACKAGE_STRING': None,
+ 'PACKAGE_STRING': project.lower() + ' latest',
'PACKAGE_URL': 'https://frrouting.org/',
- 'PACKAGE_VERSION': None,
- 'INSTALL_PREFIX_ETC': None,
- 'INSTALL_PREFIX_SBIN': None,
- 'INSTALL_PREFIX_STATE': None,
- 'INSTALL_PREFIX_MODULES': None,
- 'INSTALL_USER': None,
- 'INSTALL_GROUP': None,
- 'INSTALL_VTY_GROUP': None,
+ 'PACKAGE_VERSION': 'latest',
+ 'INSTALL_PREFIX_ETC': '/etc/frr',
+ 'INSTALL_PREFIX_SBIN': '/usr/lib/frr',
+ 'INSTALL_PREFIX_STATE': '/var/run/frr',
+ 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules',
+ 'INSTALL_USER': 'frr',
+ 'INSTALL_GROUP': 'frr',
+ 'INSTALL_VTY_GROUP': 'frrvty',
'GROUP': 'frr',
'USER': 'frr',
}
# extract version information, installation location, other stuff we need to
# use when building final documents
val = re.compile('^S\["([^"]+)"\]="(.*)"$')
-with open('../../config.status', 'r') as cfgstatus:
- for ln in cfgstatus.readlines():
- m = val.match(ln)
- if not m or m.group(1) not in replace_vars.keys(): continue
- replace_vars[m.group(1)] = m.group(2)
+try:
+ with open('../../config.status', 'r') as cfgstatus:
+ for ln in cfgstatus.readlines():
+ m = val.match(ln)
+ if not m or m.group(1) not in replace_vars.keys(): continue
+ replace_vars[m.group(1)] = m.group(2)
+except IOError:
+ # if config.status doesn't exist, just ignore it
+ pass
# manually fill out some of these we can't get from config.status
replace_vars['COPYRIGHT_STR'] = "Copyright (c)"
div.body {
max-width: none;
}
+
+pre {
+ background-color: #e2e2e2;
+}
Each of the daemons has its own config file. The daemon name plus ``.conf`` is
the default config file name. For example, zebra's default config file name is
:file:`zebra.conf`. You can specify a config file using the :option:`-f` or
-:option:`--config-file` options when starting the daemon.
+:option:`--config_file` options when starting the daemon.
.. _basic-config-commands:
Below is a sample configuration file for the zebra daemon.
-::
+.. code-block:: frr
- !
- ! Zebra configuration file
- !
- hostname Router
- password zebra
- enable password zebra
- !
- log stdout
- !
- !
+ !
+ ! Zebra configuration file
+ !
+ hostname Router
+ password zebra
+ enable password zebra
+ !
+ log stdout
+ !
+ !
'!' and '#' are comment characters. If the first character of the word
is one of the comment characters then from the rest of the line forward
will be ignored as a comment.
-::
+.. code-block:: frr
- password zebra!password
+ password zebra!password
If a comment character is not the first character of the word, it's a
normal character. So in the above example '!' will not be regarded as a
These options apply to all |PACKAGE_NAME| daemons.
-.. option:: -d
-.. option:: --daemon
+.. option:: -d, --daemon
Run in daemon mode.
-.. option:: -f <file>
-.. option:: --config-file <file>
+.. option:: -f, --config_file <file>
Set configuration file name.
Display this help and exit.
-.. option:: -i <file>
-.. option:: --pid-file <file>
+.. option:: -i, --pid_file <file>
Upon startup the process identifier of the daemon is written to a file,
typically in :file:`/var/run`. This file can be used by the init system
machine can be used to collect differing routing views from differing
points in the network.
-.. option:: -A <address>
-.. option:: --vty-addr <address>
+.. option:: -A, --vty_addr <address>
Set the VTY local address to bind to. If set, the VTY socket will only
be bound to this address.
-.. option:: -P <port>
-.. option:: --vty-port <port>
+.. option:: -P, --vty_port <port>
Set the VTY TCP port number. If set to 0 then the TCP VTY sockets will not
be opened.
.. option:: -u <user>
-.. option:: --vty_addr <user>
Set the user and group to run as.
-.. option:: -v
-.. option:: --version
+.. option:: -v, --version
Print program version.
the following command line option at daemon startup:
-.. option:: -M <module:options>
-.. option:: --module <module:options>
+.. option:: -M, --module <module:options>
Load the specified module, optionally passing options to it. If the module
name contains a slash (/), it is assumed to be a full pathname to a file to
::
- % telnet localhost 2601
- Trying 127.0.0.1...
- Connected to localhost.
- Escape character is '^]'.
+ % telnet localhost 2601
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
- Hello, this is |PACKAGE_NAME| (version |PACKAGE_VERSION|)
- |COPYRIGHT_STR|
+ Hello, this is |PACKAGE_NAME| (version |PACKAGE_VERSION|)
+ |COPYRIGHT_STR|
- User Access Verification
+ User Access Verification
- Password: XXXXX
- Router> ?
- enable . . . Turn on privileged commands
- exit . . . Exit current mode and down to previous mode
- help . . . Description of the interactive help system
- list . . . Print command list
- show . . . Show system inform
+ Password: XXXXX
+ Router> ?
+ enable . . . Turn on privileged commands
+ exit . . . Exit current mode and down to previous mode
+ help . . . Description of the interactive help system
+ list . . . Print command list
+ show . . . Show system inform
- wh. . . Display who is on a vty
- Router> enable
- Password: XXXXX
- Router# configure terminal
- Router(config)# interface eth0
- Router(config-if)# ip address 10.0.0.1/8
- Router(config-if)# ^Z
- Router#
+ wh. . . Display who is on a vty
+ Router> enable
+ Password: XXXXX
+ Router# configure terminal
+ Router(config)# interface eth0
+ Router(config-if)# ip address 10.0.0.1/8
+ Router(config-if)# ^Z
+ Router#
:kbd:`?` and the ``find`` command are very useful for looking up commands.
means press the Control Key.
:kbd:`C-f` / :kbd:`LEFT`
- Move forward one character.
+ Move forward one character.
:kbd:`C-b` / :kbd:`RIGHT`
- Move backward one character.
+ Move backward one character.
:kbd:`M-f`
- Move forward one word.
+ Move forward one word.
:kbd:`M-b`
- Move backward one word.
+ Move backward one word.
:kbd:`C-a`
- Move to the beginning of the line.
+ Move to the beginning of the line.
:kbd:`C-e`
- Move to the end of the line.
+ Move to the end of the line.
.. _cli-editing-commands:
:kbd:`C-h` / :kbd:`DEL`
- Delete the character before point.
+ Delete the character before point.
:kbd:`C-d`
- Delete the character after point.
+ Delete the character after point.
:kbd:`M-d`
- Forward kill word.
+ Forward kill word.
:kbd:`C-w`
- Backward kill word.
+ Backward kill word.
:kbd:`C-k`
- Kill to the end of the line.
+ Kill to the end of the line.
:kbd:`C-u`
- Kill line from the beginning, erasing input.
+ Kill line from the beginning, erasing input.
:kbd:`C-t`
- Transpose character.
+ Transpose character.
CLI Advanced Commands
:kbd:`C-c`
- Interrupt current input and moves to the next line.
+ Interrupt current input and moves to the next line.
:kbd:`C-z`
- End current configuration session and move to top node.
+ End current configuration session and move to top node.
:kbd:`C-n` / :kbd:`DOWN`
- Move down to next line in the history buffer.
+ Move down to next line in the history buffer.
:kbd:`C-p` / :kbd:`UP`
- Move up to previous line in the history buffer.
+ Move up to previous line in the history buffer.
:kbd:`TAB`
- Use command line completion by typing :kbd:`TAB`.
+ Use command line completion by typing :kbd:`TAB`.
:kbd:`?`
- You can use command line help by typing `help` at the beginning of
- the line. Typing :kbd:`?` at any point in the line will show possible
- completions.
+ You can use command line help by typing ``help`` at the beginning of the
+ line. Typing :kbd:`?` at any point in the line will show possible
+ completions.
.. program:: bgpd
-.. option:: -p <port>
-.. option:: --bgp_port <port>
+.. option:: -p, --bgp_port <port>
Set the bgp protocol's port number. When port number is 0, that means do not
listen bgp port.
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When program terminates, retain BGP routes added by zebra.
-.. option:: -l
-.. option:: --listenon
+.. option:: -l, --listenon
Specify a specific IP address for bgpd to listen on, rather than its
default of INADDR_ANY / IN6ADDR_ANY. This can be useful to constrain bgpd
.. index:: network A.B.C.D/M
.. clicmd:: network A.B.C.D/M
- This command adds the announcement network.::
+ This command adds the announcement network.
- router bgp 1
- address-family ipv4 unicast
- network 10.0.0.0/8
- exit-address-family
+ .. code-block:: frr
+
+ router bgp 1
+ address-family ipv4 unicast
+ network 10.0.0.0/8
+ exit-address-family
This configuration example says that network 10.0.0.0/8 will be
announced to all neighbors. Some vendors' routers don't advertise
.. clicmd:: neighbor PEER remote-as ASN
Creates a new neighbor whose remote-as is ASN. PEER can be an IPv4 address
- or an IPv6 address or an interface to use for the connection.::
+ or an IPv6 address or an interface to use for the connection.
- router bgp 1
- neighbor 10.0.0.1 remote-as 2
+ .. code-block:: frr
+
+ router bgp 1
+ neighbor 10.0.0.1 remote-as 2
In this case my router, in AS-1, is trying to peer with AS-2 at 10.0.0.1.
This command must be the first command used when configuring a neighbor. If
- the remote-as is not specified, *bgpd* will complain like this:::
+ the remote-as is not specified, *bgpd* will complain like this: ::
can't find neighbor 10.0.0.1
Specify the IPv4 source address to use for the :abbr:`BGP` session to this
neighbour, may be specified as either an IPv4 address directly or as an
interface name (in which case the *zebra* daemon MUST be running in order
- for *bgpd* to be able to retrieve interface state).::
+ for *bgpd* to be able to retrieve interface state).
+
+ .. code-block:: frr
router bgp 64555
neighbor foo update-source 192.168.0.1
attribute. AS 7675 provides upstream Internet connection to AS 100.
When following configuration exists in AS 7675, AS 100 networks
operator can set local preference in AS 7675 network by setting BGP
-communities attribute to the updates.::
+communities attribute to the updates.
+
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
Following configuration announce 10.0.0.0/8 from AS 100 to AS 7675.
The route has communities value 7675:80 so when above configuration
exists in AS 7675, announced route's local preference will be set to
-value 80.::
+value 80.
+
+.. code-block:: frr
router bgp 100
network 10.0.0.0/8
communities attribute. This configuration only permit BGP routes
which has BGP communities value 0:80 or 0:90. Network operator can
put special internal communities value at BGP border router, then
-limit the BGP routes announcement into the internal network.::
+limit the BGP routes announcement into the internal network.
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
Following exmaple filter BGP routes which has communities value 1:1.
When there is no match community-list returns deny. To avoid
-filtering all of routes, we need to define permit any at last.::
+filtering all of routes, we need to define permit any at last.
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
standard community lists. In below example `internet` act as
match any. It matches all of BGP routes even if the route does not
have communities attribute at all. So community list ``INTERNET``
-is same as above example's ``FILTER``.::
+is same as above example's ``FILTER``.
+
+.. code-block:: frr
ip community-list standard INTERNET deny 1:1
ip community-list standard INTERNET permit internet
Following configuration is an example of communities value deletion.
With this configuration communities value 100:1 and 100:2 is removed
from BGP updates. For communities value deletion, only `permit`
-community-list is used. `deny` community-list is ignored.::
+community-list is used. `deny` community-list is ignored.
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
.. clicmd:: show ip extcommunity-list NAME
This command displays current extcommunity-list information. When `name` is
- specified the community list's information is shown.
-
-::
+ specified the community list's information is shown.::
- # show ip extcommunity-list
+ # show ip extcommunity-list
.. _bgp-extended-communities-in-route-map:
Deletes any previously-configured import or export route-target list.
-.. index:: label vpn export (0..1048575)
-.. clicmd:: label vpn export (0..1048575)
+.. index:: label vpn export (0..1048575)|auto
+.. clicmd:: label vpn export (0..1048575)|auto
Specifies an optional MPLS label to be attached to a route exported from the
- current unicast VRF to VPN.
+ current unicast VRF to VPN. If label is specified as ``auto``, the label
+ value is automatically assigned from a pool maintained by the zebra
+ daemon. If zebra is not running, automatic label assignment will not
+ complete, which will block corresponding route export.
-.. index:: no label vpn export [(0..1048575)]
-.. clicmd:: no label vpn export [(0..1048575)]
+.. index:: no label vpn export [(0..1048575)|auto]
+.. clicmd:: no label vpn export [(0..1048575)|auto]
Deletes any previously-configured export label.
not sent to the neighbor. When ``bgp config-type cisco`` is specified, the
community attribute is not sent to the neighbor by default. To send the
community attribute user has to specify *neighbor A.B.C.D send-community*
-command.::
+command.
+
+.. code-block:: frr
!
router bgp 1
Make a new BGP instance. You can use an arbitrary word for the `name`.
- ::
+ .. code-block:: frr
- bgp multiple-instance
- !
- router bgp 1
- neighbor 10.0.0.1 remote-as 2
- neighbor 10.0.0.2 remote-as 3
- !
- router bgp 2
- neighbor 10.0.0.3 remote-as 4
- neighbor 10.0.0.4 remote-as 5
+ bgp multiple-instance
+ !
+ router bgp 1
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.2 remote-as 3
+ !
+ router bgp 2
+ neighbor 10.0.0.3 remote-as 4
+ neighbor 10.0.0.4 remote-as 5
BGP view is almost same as normal BGP process. The result of route selection
With this command, you can setup Route Server like below.
- ::
+ .. code-block:: frr
bgp multiple-instance
!
--------------
You can set different routing policy for a peer. For example, you can set
-different filter for a peer.::
+different filter for a peer.
+
+.. code-block:: frr
bgp multiple-instance
!
How to set up a 6-Bone connection
=================================
-::
+.. code-block:: frr
- bgpd configuration
- ==================
+ ! bgpd configuration
+ ! ==================
!
! MP-BGP configuration
!
BGP Configuration Examples
==========================
-Example of a session to an upstream, advertising only one prefix to it.::
+Example of a session to an upstream, advertising only one prefix to it.
+
+.. code-block:: frr
router bgp 64512
bgp router-id 10.236.87.1
as guidance only, it has NOT been tested and almost certainly containts silly
mistakes, if not serious flaws.
-::
+.. code-block:: frr
router bgp 64512
bgp router-id 10.236.87.1
import sys
import os
import re
+import pygments
+from sphinx.highlighting import lexers
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# General information about the project.
project = u'FRR'
copyright = u'2017, FRR'
-author = u'FRR'
+author = u'FRR authors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# Extract values from codebase for substitution into docs.
# -----------------------------------------------------------------------------
-# Various installation prefixes. Reasonable defaults are set where possible.
-# Values are overridden by logic below.
+# Various installation prefixes. Values are extracted from config.status.
+# Reasonable defaults are set in case that file does not exist.
replace_vars = {
- 'AUTHORS': 'Kunihiro Ishiguro, et al.',
+ 'AUTHORS': author,
'COPYRIGHT_YEAR': '1999-2005',
- 'COPYRIGHT_STR': None,
+ 'COPYRIGHT_STR': 'Copyright (c) 1999-2005',
'PACKAGE_NAME': project.lower(),
'PACKAGE_TARNAME': project.lower(),
- 'PACKAGE_STRING': None,
+ 'PACKAGE_STRING': project.lower() + ' latest',
'PACKAGE_URL': 'https://frrouting.org/',
- 'PACKAGE_VERSION': None,
- 'INSTALL_PREFIX_ETC': None,
- 'INSTALL_PREFIX_SBIN': None,
- 'INSTALL_PREFIX_STATE': None,
- 'INSTALL_PREFIX_MODULES': None,
- 'INSTALL_USER': None,
- 'INSTALL_GROUP': None,
- 'INSTALL_VTY_GROUP': None,
+ 'PACKAGE_VERSION': 'latest',
+ 'INSTALL_PREFIX_ETC': '/etc/frr',
+ 'INSTALL_PREFIX_SBIN': '/usr/lib/frr',
+ 'INSTALL_PREFIX_STATE': '/var/run/frr',
+ 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules',
+ 'INSTALL_USER': 'frr',
+ 'INSTALL_GROUP': 'frr',
+ 'INSTALL_VTY_GROUP': 'frrvty',
+ 'GROUP': 'frr',
+ 'USER': 'frr',
}
# extract version information, installation location, other stuff we need to
# use when building final documents
val = re.compile('^S\["([^"]+)"\]="(.*)"$')
-with open('../../config.status', 'r') as cfgstatus:
- for ln in cfgstatus.readlines():
- m = val.match(ln)
- if not m or m.group(1) not in replace_vars.keys(): continue
- replace_vars[m.group(1)] = m.group(2)
+try:
+ with open('../../config.status', 'r') as cfgstatus:
+ for ln in cfgstatus.readlines():
+ m = val.match(ln)
+ if not m or m.group(1) not in replace_vars.keys(): continue
+ replace_vars[m.group(1)] = m.group(2)
+except IOError:
+ # if config.status doesn't exist, just ignore it
+ pass
# manually fill out some of these we can't get from config.status
replace_vars['COPYRIGHT_STR'] = "Copyright (c)"
app.add_object_type('clicmd', 'clicmd')
# css overrides for HTML theme
app.add_stylesheet('overrides.css')
+ # load Pygments lexer for FRR config syntax
+ #
+ # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we
+ # do it manually since not all of our supported build platforms have 2.2
+ # yet.
+ #
+ # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer")
+ custom_namespace = {}
+ exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace)
+ lexers['frr'] = custom_namespace['FRRLexer']()
.. program:: eigrpd
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When the program terminates, retain routes added by *eigrpd*.
Below is very simple EIGRP configuration. Interface `eth0` and
interface which address match to `10.0.0.0/8` are EIGRP enabled.
- ::
+ .. code-block:: frr
!
router eigrp 1
Basic filtering is done by `access-list` as shown in the
following example.
- ::
+ .. code-block:: frr
access-list filter deny 10.0.0.0/9
access-list filter permit 10.0.0.0/8
ISIS Configuration Examples
===========================
-A simple example, with MD5 authentication enabled:::
+A simple example, with MD5 authentication enabled:
+
+.. code-block:: frr
!
interface eth0
A Traffic Engineering configuration, with Inter-ASv2 support.
-First, the 'zebra.conf' part:::
+First, the :file:`zebra.conf` part:
+
+.. code-block:: frr
hostname HOSTNAME
password PASSWORD
neighbor 10.1.1.2 as 65000
-Then the 'isisd.conf' itself:::
+Then the :file:`isisd.conf` itself:
+
+.. code-block:: frr
hostname HOSTNAME
password PASSWORD
routing protocol (e.g. iBGP) to allow hubs to be able to relay all traffic.
This can be achieved in hubs with the following bgp configuration (network
-command defines the GRE subnet):::
+command defines the GRE subnet):
+
+.. code-block:: frr
router bgp 65555
address-family ipv4 unicast
traffic that is routed from gre1 back to gre1 in rate limited manner.
This can be achieved with the following iptables rule.
-::
+.. code-block:: shell
- iptables -A FORWARD -i gre1 -o gre1 \\
- -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\
- --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\
- --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
+ iptables -A FORWARD -i gre1 -o gre1 \\
+ -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\
+ --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\
+ --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
You can fine tune the src/dstmask according to the prefix lengths you
if needed. However, the above should be good in most cases.
This kernel NFLOG target's nflog-group is configured in global nhrp config
-with:::
+with:
+
+.. code-block:: frr
- nhrp nflog-group 1
+ nhrp nflog-group 1
To start sending these traffic notices out from hubs, use the nhrp
-per-interface directive:::
+per-interface directive:
+
+.. code-block:: frr
+
+ interface gre1
+ ip nhrp redirect
- interface gre1
- ip nhrp redirect
.. _integration-with-ike:
will cause the holdtime to be increased by `initial-holdtime`, bounded
by the `maximum-holdtime` configured with this command. If the adaptive
hold-time elapses without any SPF-triggering event occuring then
- the current holdtime is reset to the `initial-holdtime`.::
+ the current holdtime is reset to the `initial-holdtime`.
+
+ .. code-block:: frr
router ospf6
timers throttle spf 200 400 10000
Example of ospf6d configured on one interface and area:
-::
+.. code-block:: frr
interface eth0
ipv6 ospf6 instance-id 0
::
- # show ip ospf database router 192.168.0.49
-
- OSPF Router with ID (192.168.0.53)
-
- Router Link States (Area 0.0.0.0)
-
- LS age: 38
- Options: 0x2 : *|-|-|-|-|-|E|*
- LS Flags: 0x6
- Flags: 0x2 : ASBR
- LS Type: router-LSA
- Link State ID: 192.168.0.49
- Advertising Router: 192.168.0.49
- LS Seq Number: 80000f90
- Checksum: 0x518b
- Length: 60
- Number of Links: 3
-
- Link connected to: a Transit Network
- (Link ID) Designated Router address: 192.168.1.3
- (Link Data) Router Interface address: 192.168.1.3
- Number of TOS metrics: 0
- TOS 0 Metric: 10
-
- Link connected to: a Transit Network
- (Link ID) Designated Router address: 192.168.0.49
- (Link Data) Router Interface address: 192.168.0.49
- Number of TOS metrics: 0
- TOS 0 Metric: 10
-
- Link connected to: Stub Network
- (Link ID) Net: 192.168.3.190
- (Link Data) Network Mask: 255.255.255.255
- Number of TOS metrics: 0
- TOS 0 Metric: 39063
- # show ip ospf database network 192.168.0.49
-
- OSPF Router with ID (192.168.0.53)
-
- Net Link States (Area 0.0.0.0)
-
- LS age: 285
- Options: 0x2 : *|-|-|-|-|-|E|*
- LS Flags: 0x6
- LS Type: network-LSA
- Link State ID: 192.168.0.49 (address of Designated Router)
- Advertising Router: 192.168.0.49
- LS Seq Number: 80000074
- Checksum: 0x0103
- Length: 40
- Network Mask: /29
- Attached Router: 192.168.0.49
- Attached Router: 192.168.0.52
- Attached Router: 192.168.0.53
- Attached Router: 192.168.0.54
+ # show ip ospf database router 192.168.0.49
+
+ OSPF Router with ID (192.168.0.53)
+
+ Router Link States (Area 0.0.0.0)
+
+ LS age: 38
+ Options: 0x2 : *|-|-|-|-|-|E|*
+ LS Flags: 0x6
+ Flags: 0x2 : ASBR
+ LS Type: router-LSA
+ Link State ID: 192.168.0.49
+ Advertising Router: 192.168.0.49
+ LS Seq Number: 80000f90
+ Checksum: 0x518b
+ Length: 60
+ Number of Links: 3
+
+ Link connected to: a Transit Network
+ (Link ID) Designated Router address: 192.168.1.3
+ (Link Data) Router Interface address: 192.168.1.3
+ Number of TOS metrics: 0
+ TOS 0 Metric: 10
+
+ Link connected to: a Transit Network
+ (Link ID) Designated Router address: 192.168.0.49
+ (Link Data) Router Interface address: 192.168.0.49
+ Number of TOS metrics: 0
+ TOS 0 Metric: 10
+
+ Link connected to: Stub Network
+ (Link ID) Net: 192.168.3.190
+ (Link Data) Network Mask: 255.255.255.255
+ Number of TOS metrics: 0
+ TOS 0 Metric: 39063
+ # show ip ospf database network 192.168.0.49
+
+ OSPF Router with ID (192.168.0.53)
+
+ Net Link States (Area 0.0.0.0)
+
+ LS age: 285
+ Options: 0x2 : *|-|-|-|-|-|E|*
+ LS Flags: 0x6
+ LS Type: network-LSA
+ Link State ID: 192.168.0.49 (address of Designated Router)
+ Advertising Router: 192.168.0.49
+ LS Seq Number: 80000074
+ Checksum: 0x0103
+ Length: 40
+ Network Mask: /29
+ Attached Router: 192.168.0.49
+ Attached Router: 192.168.0.52
+ Attached Router: 192.168.0.53
+ Attached Router: 192.168.0.54
Note that from one LSA, you can find the other. E.g. Given the
::
- ------------------------ Network: ......
- | Designated Router IP: 192.168.1.3
- |
- IP: 192.168.1.3
- (transit link)
- (cost: 10)
- Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32
- (cost: 10) (cost: 39063)
- (transit link)
- IP: 192.168.0.49
- |
- |
- ------------------------------ Network: 192.168.0.48/29
- | | | Designated Router IP: 192.168.0.49
- | | |
- | | Router ID: 192.168.0.54
- | |
- | Router ID: 192.168.0.53
- |
- Router ID: 192.168.0.52
+ ------------------------ Network: ......
+ | Designated Router IP: 192.168.1.3
+ |
+ IP: 192.168.1.3
+ (transit link)
+ (cost: 10)
+ Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32
+ (cost: 10) (cost: 39063)
+ (transit link)
+ IP: 192.168.0.49
+ |
+ |
+ ------------------------------ Network: 192.168.0.48/29
+ | | | Designated Router IP: 192.168.0.49
+ | | |
+ | | Router ID: 192.168.0.54
+ | |
+ | Router ID: 192.168.0.53
+ |
+ Router ID: 192.168.0.52
Note the Router IDs, though they look like IP addresses and often are
::
- # show ip ospf database external 192.168.165.0
- LS age: 995
- Options: 0x2 : *|-|-|-|-|-|E|*
- LS Flags: 0x9
- LS Type: AS-external-LSA
- Link State ID: 192.168.165.0 (External Network Number)
- Advertising Router: 192.168.0.49
- LS Seq Number: 800001d8
- Checksum: 0xea27
- Length: 36
- Network Mask: /24
- Metric Type: 2 (Larger than any link state path)
- TOS: 0
- Metric: 20
- Forward Address: 0.0.0.0
- External Route Tag: 0
+ # show ip ospf database external 192.168.165.0
+ LS age: 995
+ Options: 0x2 : *|-|-|-|-|-|E|*
+ LS Flags: 0x9
+ LS Type: AS-external-LSA
+ Link State ID: 192.168.165.0 (External Network Number)
+ Advertising Router: 192.168.0.49
+ LS Seq Number: 800001d8
+ Checksum: 0xea27
+ Length: 36
+ Network Mask: /24
+ Metric Type: 2 (Larger than any link state path)
+ TOS: 0
+ Metric: 20
+ Forward Address: 0.0.0.0
+ External Route Tag: 0
We can add this to our partial topology from above, which now looks
holdtime can be viewed with :clicmd:`show ip ospf`, where it is expressed as
a multiplier of the `initial-holdtime`.
- ::
+ .. code-block:: frr
router ospf
timers throttle spf 200 400 10000
on this interface so router can provide network information to the other
ospf routers via this interface.
-::
-
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
+ .. code-block:: frr
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
Prefix length in interface must be equal or bigger (ie. smaller network) than
prefix length in network statement. For example statement above doesn't enable
.. index:: no area (0-4294967295) range A.B.C.D/M
.. clicmd:: no area (0-4294967295) range A.B.C.D/M
- Summarize intra area paths from specified area into one Type-3 summary-LSA
- announced to other areas. This command can be used only in ABR and ONLY
- router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can
- be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS.
- Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR.
+ Summarize intra area paths from specified area into one Type-3 summary-LSA
+ announced to other areas. This command can be used only in ABR and ONLY
+ router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can
+ be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS.
+ Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR.
-::
+ .. code-block:: frr
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
- network 10.0.0.0/8 area 0.0.0.10
- area 0.0.0.10 range 10.0.0.0/8
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8
- With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is
- announced into backbone area if area 0.0.0.10 contains at least one intra-area
- network (ie. described with router or network LSA) from this range.
+ With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is
+ announced into backbone area if area 0.0.0.10 contains at least one intra-area
+ network (ie. described with router or network LSA) from this range.
.. index:: area A.B.C.D range IPV4_PREFIX not-advertise
.. clicmd:: area A.B.C.D range IPV4_PREFIX not-advertise
Substitute summarized prefix with another prefix.
-::
+ .. code-block:: frr
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
- network 10.0.0.0/8 area 0.0.0.10
- area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8
One Type-3 summary-LSA with routing info 11.0.0.0/8 is announced into backbone area if
Filter Type-3 summary-LSAs announced to other areas originated from intra-
area paths from specified area.
-::
-
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
- network 10.0.0.0/8 area 0.0.0.10
- area 0.0.0.10 export-list foo
- !
- access-list foo permit 10.10.0.0/16
- access-list foo deny any
+ .. code-block:: frr
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 export-list foo
+ !
+ access-list foo permit 10.10.0.0/16
+ access-list foo deny any
With example above any intra-area paths from area 0.0.0.10 and from range
10.10.0.0/16 (for example 10.10.1.0/24 and 10.10.2.128/30) are announced into
.. index:: no area (0-4294967295) import-list NAME
.. clicmd:: no area (0-4294967295) import-list NAME
- Same as export-list, but it applies to paths announced into specified area as
- Type-3 summary-LSAs.
+ Same as export-list, but it applies to paths announced into specified area
+ as Type-3 summary-LSAs.
.. index:: area A.B.C.D filter-list prefix NAME in
.. clicmd:: area A.B.C.D filter-list prefix NAME in
.. index:: no area (0-4294967295) filter-list prefix NAME out
.. clicmd:: no area (0-4294967295) filter-list prefix NAME out
- Filtering Type-3 summary-LSAs to/from area using prefix lists. This command
- makes sense in ABR only.
+ Filtering Type-3 summary-LSAs to/from area using prefix lists. This command
+ makes sense in ABR only.
.. index:: area A.B.C.D authentication
.. clicmd:: area A.B.C.D authentication
.. index:: no area (0-4294967295) authentication
.. clicmd:: no area (0-4294967295) authentication
- Specify that simple password authentication should be used for the given
- area.
+ Specify that simple password authentication should be used for the given
+ area.
.. index:: area A.B.C.D authentication message-digest
.. clicmd:: area A.B.C.D authentication message-digest
Set OSPF authentication key to a cryptographic password. The cryptographic
algorithm is MD5.
- KEYID identifies secret key used to create the message digest. This ID
- is part of the protocol and must be consistent across routers on a
- link.
+ KEYID identifies secret key used to create the message digest. This ID is
+ part of the protocol and must be consistent across routers on a link.
- KEY is the actual message digest key, of up to 16 chars (larger strings
- will be truncated), and is associated with the given KEYID.
+ KEY is the actual message digest key, of up to 16 chars (larger strings will
+ be truncated), and is associated with the given KEYID.
.. index:: ip ospf cost (1-65535)
.. clicmd:: ip ospf cost (1-65535)
.. index:: no ip ospf cost
.. clicmd:: no ip ospf cost
- Set link cost for the specified interface. The cost value is set to router-LSA's
- metric field and used for SPF calculation.
+ Set link cost for the specified interface. The cost value is set to
+ router-LSA's metric field and used for SPF calculation.
.. index:: ip ospf dead-interval (1-65535)
.. clicmd:: ip ospf dead-interval (1-65535)
.. index:: no ip ospf priority
.. clicmd:: no ip ospf priority
- Set RouterPriority integer value. The router with the highest priority
- will be more eligible to become Designated Router. Setting the value
- to 0, makes the router ineligible to become Designated Router. The
- default value is 1.
+ Set RouterPriority integer value. The router with the highest priority will
+ be more eligible to become Designated Router. Setting the value to 0, makes
+ the router ineligible to become Designated Router. The default value is 1.
.. index:: ip ospf retransmit-interval (1-65535)
.. clicmd:: ip ospf retransmit-interval (1-65535)
.. index:: no ip ospf retransmit interval
.. clicmd:: no ip ospf retransmit interval
- Set number of seconds for RxmtInterval timer value. This value is used
- when retransmitting Database Description and Link State Request packets.
- The default value is 5 seconds.
+ Set number of seconds for RxmtInterval timer value. This value is used when
+ retransmitting Database Description and Link State Request packets. The
+ default value is 5 seconds.
.. index:: ip ospf transmit-delay
.. clicmd:: ip ospf transmit-delay
.. clicmd:: no ip ospf transmit-delay
Set number of seconds for InfTransDelay value. LSAs' age should be
- incremented by this value when transmitting.
- The default value is 1 seconds.
+ incremented by this value when transmitting. The default value is 1 second.
.. index:: ip ospf area (A.B.C.D|(0-4294967295))
.. clicmd:: ip ospf area (A.B.C.D|(0-4294967295))
.. index:: no ip ospf area
.. clicmd:: no ip ospf area
- Enable ospf on an interface and set associated area.
+ Enable ospf on an interface and set associated area.
.. _redistribute-routes-to-ospf:
.. _ospf-redistribute:
- Redistribute routes of the specified protocol
- or kind into OSPF, with the metric type and metric set if specified,
- filtering the routes using the given route-map if specified.
- Redistributed routes may also be filtered with distribute-lists, see
+ Redistribute routes of the specified protocol or kind into OSPF, with the
+ metric type and metric set if specified, filtering the routes using the
+ given route-map if specified. Redistributed routes may also be filtered
+ with distribute-lists, see
:ref:`ospf distribute-list configuration <ospf-distribute-list>`.
- Redistributed routes are distributed as into OSPF as Type-5 External
- LSAs into links to areas that accept external routes, Type-7 External LSAs
- for NSSA areas and are not redistributed at all into Stub areas, where
- external routes are not permitted.
+ Redistributed routes are distributed as into OSPF as Type-5 External LSAs
+ into links to areas that accept external routes, Type-7 External LSAs for
+ NSSA areas and are not redistributed at all into Stub areas, where external
+ routes are not permitted.
Note that for connected routes, one may instead use the `passive-interface`
configuration.
.. index:: no default-information originate
.. clicmd:: no default-information originate
- Originate an AS-External (type-5) LSA describing a default route into
- all external-routing capable areas, of the specified metric and metric
- type. If the 'always' keyword is given then the default is always
- advertised, even when there is no default present in the routing table.
+ Originate an AS-External (type-5) LSA describing a default route into all
+ external-routing capable areas, of the specified metric and metric type. If
+ the 'always' keyword is given then the default is always advertised, even
+ when there is no default present in the routing table.
.. index:: distribute-list NAME out (kernel|connected|static|rip|ospf
.. clicmd:: distribute-list NAME out (kernel|connected|static|rip|ospf
.. _ospf-distribute-list:
- Apply the access-list filter, NAME, to
- redistributed routes of the given type before allowing the routes to
- redistributed into OSPF (:ref:`ospf redistribution <ospf-redistribute>`).
+ Apply the access-list filter, NAME, to redistributed routes of the given
+ type before allowing the routes to redistributed into OSPF
+ (:ref:`ospf redistribution <ospf-redistribute>`).
.. index:: default-metric (0-16777214)
.. clicmd:: default-metric (0-16777214)
.. index:: show ip ospf route
.. clicmd:: show ip ospf route
- Show the OSPF routing table, as determined by the most recent SPF calculation.
+ Show the OSPF routing table, as determined by the most recent SPF
+ calculation.
.. _opaque-lsa:
.. index:: no capability opaque
.. clicmd:: no capability opaque
- *ospfd* support Opaque LSA (RFC2370) as fondment for MPLS Traffic Engineering
- LSA. Prior to used MPLS TE, opaque-lsa must be enable in the configuration
- file. Alternate command could be "mpls-te on"
+ *ospfd* support Opaque LSA (:rfc:`2370`) as fondment for MPLS Traffic
+ Engineering LSA. Prior to used MPLS TE, opaque-lsa must be enable in the
+ configuration file. Alternate command could be "mpls-te on"
(:ref:`ospf-traffic-engineering`).
.. index:: show ip ospf database (opaque-link|opaque-area|opaque-external)
.. index:: no pce scope
.. clicmd:: no pce scope
- The commands are conform to :rfc:`5088` and allow OSPF router announce Path
- Compuatation Elemenent (PCE) capabilities through the Router Information (RI)
- LSA. Router Information must be enable prior to this. The command set/unset
- respectively the PCE IP adress, Autonomous System (AS) numbers of controlled
- domains, neighbor ASs, flag and scope. For flag and scope, please refer to
- :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor' command
- could be specified in order to specify all PCE neighbours.
+ The commands are conform to :rfc:`5088` and allow OSPF router announce Path
+ Compuatation Elemenent (PCE) capabilities through the Router Information
+ (RI) LSA. Router Information must be enable prior to this. The command
+ set/unset respectively the PCE IP adress, Autonomous System (AS) numbers of
+ controlled domains, neighbor ASs, flag and scope. For flag and scope, please
+ refer to :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor'
+ command could be specified in order to specify all PCE neighbours.
.. index:: show ip ospf router-info
.. clicmd:: show ip ospf router-info
Show Router Capabilities flag.
+
.. index:: show ip ospf router-info pce
.. clicmd:: show ip ospf router-info pce
.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]
.. clicmd:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]
- Set the Segment Rounting index for the specifyed prefix. Note
- that, only prefix with /32 corresponding to a loopback interface are
- currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that
- allows SR node to request to its neighbor to not pop the label.
+ Set the Segment Rounting index for the specifyed prefix. Note that, only
+ prefix with /32 corresponding to a loopback interface are currently
+ supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR
+ node to request to its neighbor to not pop the label.
.. index:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json]
.. clicmd:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json]
A simple example, with MD5 authentication enabled:
-::
+.. code-block:: frr
!
interface bge0
An :abbr:`ABR` router, with MD5 authentication and performing summarisation
of networks between the areas:
-::
+.. code-block:: frr
!
password ABCDEF
A Traffic Engineering configuration, with Inter-ASv2 support.
-First, the 'zebra.conf' part:::
+First, the :file:`zebra.conf` part:
+
+.. code-block:: frr
interface eth0
ip address 198.168.1.1/24
unrsv-bw 7 1.25e+06
neighbor 192.168.2.2 as 65000
-Then the 'ospfd.conf' itself:::
+Then the :file:`ospfd.conf` itself:
+
+.. code-block:: frr
hostname HOSTNAME
password PASSWORD
!
line vty
+A router information example with PCE advsertisement:
-A router information example with PCE advsertisement:::
+.. code-block:: frr
!
router ospf
::
- +----+ +----+ +-----+ +-----+
- |bgpd| |ripd| |ospfd| |zebra|
- +----+ +----+ +-----+ +-----+
- |
- +---------------------------|--+
- | v |
- | UNIX Kernel routing table |
- | |
- +------------------------------+
-
- FRR System Architecture
+ +----+ +----+ +-----+ +-----+
+ |bgpd| |ripd| |ospfd| |zebra|
+ +----+ +----+ +-----+ +-----+
+ |
+ +---------------------------|--+
+ | v |
+ | UNIX Kernel routing table |
+ | |
+ +------------------------------+
+
+ FRR System Architecture
Multi-process architecture brings extensibility, modularity and
.. _pim-multicast-rib-insertion:
-PIM Multicast RIB insertion::
-=============================
+PIM Multicast RIB insertion:
+============================
In order to influence Multicast RPF lookup, it is possible to insert
into zebra routes for the Multicast RIB. These routes are only
*ripd* invocation options. Common options that can be specified
(:ref:`common-invocation-options`).
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When the program terminates, retain routes added by *ripd*.
Below is very simple RIP configuration. Interface `eth0` and interface which
address match to `10.0.0.0/8` are RIP enabled.
- ::
+ .. code-block:: frr
!
router rip
the distribute-list command. For example, in the following configuration
``eth0`` will permit only the paths that match the route 10.0.0.0/8
- ::
+ .. code-block:: frr
!
router rip
Optional argument route-map MAP_NAME can be added to each `redistribute`
statement.
-::
+.. code-block:: frr
- redistribute static [route-map MAP_NAME]
- redistribute connected [route-map MAP_NAME]
- .....
+ redistribute static [route-map MAP_NAME]
+ redistribute connected [route-map MAP_NAME]
+ .....
Cisco applies route-map _before_ routes will exported to rip route table. In
Specifiy Keyed MD5 chain.
-::
+ .. code-block:: frr
- !
- key chain test
- key 1
- key-string test
- !
- interface eth1
- ip rip authentication mode md5
- ip rip authentication key-chain test
- !
+ !
+ key chain test
+ key 1
+ key-string test
+ !
+ interface eth1
+ ip rip authentication mode md5
+ ip rip authentication key-chain test
+ !
.. _rip-timers:
A simple example of a route-map:
-::
+.. code-block:: frr
- route-map test permit 10
- match ip address 10
- set local-preference 200
+ route-map test permit 10
+ match ip address 10
+ set local-preference 200
This means that if a route matches ip access-list number 10 it's
configured, named like 'PEER-X-IN' or 'PEER-X-OUT'. For example the
configuration file for router RA could be the following:
-::
+.. code-block:: frr
#Configuration for router 'RA'
!
between them, but only with the route server. For example, RA's
configuration would turn into:
-::
+.. code-block:: frr
+
+ # Configuration for router 'RA'
+ !
+ hostname RA
+ password ****
+ !
+ router bgp 65001
+ no bgp default ipv4-unicast
+ neighbor 2001:0DB8::FFFF remote-as 65000
+ !
+ address-family ipv6
+ network 2001:0DB8:AAAA:1::/64
+ network 2001:0DB8:AAAA:2::/64
+ network 2001:0DB8:0000:1::/64
+ network 2001:0DB8:0000:2::/64
- # Configuration for router 'RA'
- !
- hostname RA
- password ****
- !
- router bgp 65001
- no bgp default ipv4-unicast
- neighbor 2001:0DB8::FFFF remote-as 65000
- !
- address-family ipv6
- network 2001:0DB8:AAAA:1::/64
- network 2001:0DB8:AAAA:2::/64
- network 2001:0DB8:0000:1::/64
- network 2001:0DB8:0000:2::/64
-
- neighbor 2001:0DB8::FFFF activate
- neighbor 2001:0DB8::FFFF soft-reconfiguration inbound
- exit-address-family
- !
- line vty
- !
+ neighbor 2001:0DB8::FFFF activate
+ neighbor 2001:0DB8::FFFF soft-reconfiguration inbound
+ exit-address-family
+ !
+ line vty
+ !
Which is logically much simpler than its initial configuration, as it now
This is a fragment of the route server configuration (we only show
the policies for client RA):
-::
+.. code-block:: frr
- # Configuration for Route Server ('RS')
- !
- hostname RS
- password ix
- !
- bgp multiple-instance
- !
- router bgp 65000 view RS
- no bgp default ipv4-unicast
- neighbor 2001:0DB8::A remote-as 65001
- neighbor 2001:0DB8::B remote-as 65002
- neighbor 2001:0DB8::C remote-as 65003
- !
- address-family ipv6
- neighbor 2001:0DB8::A activate
- neighbor 2001:0DB8::A route-server-client
- neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
- neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export
- neighbor 2001:0DB8::A soft-reconfiguration inbound
-
- neighbor 2001:0DB8::B activate
- neighbor 2001:0DB8::B route-server-client
- neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import
- neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export
- neighbor 2001:0DB8::B soft-reconfiguration inbound
-
- neighbor 2001:0DB8::C activate
- neighbor 2001:0DB8::C route-server-client
- neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import
- neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export
- neighbor 2001:0DB8::C soft-reconfiguration inbound
- exit-address-family
- !
- ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64
- ipv6 prefix-list COMMON-PREFIXES seq 10 deny any
- !
- ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64
- ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any
- !
- ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64
- ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any
- !
- ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64
- ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any
- !
- route-map RSCLIENT-A-IMPORT permit 10
- match peer 2001:0DB8::B
- call A-IMPORT-FROM-B
- route-map RSCLIENT-A-IMPORT permit 20
- match peer 2001:0DB8::C
- call A-IMPORT-FROM-C
- !
- route-map A-IMPORT-FROM-B permit 10
- match ipv6 address prefix-list COMMON-PREFIXES
- set metric 100
- route-map A-IMPORT-FROM-B permit 20
- match ipv6 address prefix-list PEER-B-PREFIXES
- set community 65001:11111
- !
- route-map A-IMPORT-FROM-C permit 10
- match ipv6 address prefix-list COMMON-PREFIXES
- set metric 200
- route-map A-IMPORT-FROM-C permit 20
- match ipv6 address prefix-list PEER-C-PREFIXES
- set community 65001:22222
- !
- route-map RSCLIENT-A-EXPORT permit 10
- match peer 2001:0DB8::B
- match ipv6 address prefix-list PEER-A-PREFIXES
- route-map RSCLIENT-A-EXPORT permit 20
- match peer 2001:0DB8::C
- match ipv6 address prefix-list PEER-A-PREFIXES
- !
- ...
- ...
- ...
+ # Configuration for Route Server ('RS')
+ !
+ hostname RS
+ password ix
+ !
+ bgp multiple-instance
+ !
+ router bgp 65000 view RS
+ no bgp default ipv4-unicast
+ neighbor 2001:0DB8::A remote-as 65001
+ neighbor 2001:0DB8::B remote-as 65002
+ neighbor 2001:0DB8::C remote-as 65003
+ !
+ address-family ipv6
+ neighbor 2001:0DB8::A activate
+ neighbor 2001:0DB8::A route-server-client
+ neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
+ neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export
+ neighbor 2001:0DB8::A soft-reconfiguration inbound
+
+ neighbor 2001:0DB8::B activate
+ neighbor 2001:0DB8::B route-server-client
+ neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import
+ neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export
+ neighbor 2001:0DB8::B soft-reconfiguration inbound
+
+ neighbor 2001:0DB8::C activate
+ neighbor 2001:0DB8::C route-server-client
+ neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import
+ neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export
+ neighbor 2001:0DB8::C soft-reconfiguration inbound
+ exit-address-family
+ !
+ ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64
+ ipv6 prefix-list COMMON-PREFIXES seq 10 deny any
+ !
+ ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64
+ ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any
+ !
+ ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64
+ ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any
+ !
+ ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64
+ ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any
+ !
+ route-map RSCLIENT-A-IMPORT permit 10
+ match peer 2001:0DB8::B
+ call A-IMPORT-FROM-B
+ route-map RSCLIENT-A-IMPORT permit 20
+ match peer 2001:0DB8::C
+ call A-IMPORT-FROM-C
+ !
+ route-map A-IMPORT-FROM-B permit 10
+ match ipv6 address prefix-list COMMON-PREFIXES
+ set metric 100
+ route-map A-IMPORT-FROM-B permit 20
+ match ipv6 address prefix-list PEER-B-PREFIXES
+ set community 65001:11111
+ !
+ route-map A-IMPORT-FROM-C permit 10
+ match ipv6 address prefix-list COMMON-PREFIXES
+ set metric 200
+ route-map A-IMPORT-FROM-C permit 20
+ match ipv6 address prefix-list PEER-C-PREFIXES
+ set community 65001:22222
+ !
+ route-map RSCLIENT-A-EXPORT permit 10
+ match peer 2001:0DB8::B
+ match ipv6 address prefix-list PEER-A-PREFIXES
+ route-map RSCLIENT-A-EXPORT permit 20
+ match peer 2001:0DB8::C
+ match ipv6 address prefix-list PEER-A-PREFIXES
+ !
+ ...
+ ...
+ ...
If you compare the initial configuration of RA with the route server
route-maps. For example suppose that in the non-route-server scenario peer
RA had the following filters configured for input from peer B:
-::
+.. code-block:: frr
neighbor 2001:0DB8::B prefix-list LIST-1 in
neighbor 2001:0DB8::B filter-list LIST-2 in
route-map). That route-map can then be used inside the Import
policy in the route server. Lets see how to do it:
-::
+.. code-block:: frr
neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
...
In the following example, the router prefers valid routes over invalid
prefixes because invalid routes have a lower local preference.
- ::
+ .. code-block:: frr
! Allow for invalid routes in route selection process
route bgp 60001
RPKI Configuration Example
--------------------------
-::
+.. code-block:: frr
hostname bgpd1
password zebra
:file:`/etc/snmp/snmpd.conf`, the ``master agentx`` directive should be added.
In each of the FRR daemons, ``agentx`` command will enable AgentX support.
-::
+:file:`/etc/snmp/snmpd.conf`:
+ #
+ # example access restrictions setup
+ #
+ com2sec readonly default public
+ group MyROGroup v1 readonly
+ view all included .1 80
+ access MyROGroup "" any noauth exact all none none
+ #
+ # enable master agent for AgentX subagents
+ #
+ master agentx
+
+:file:`/etc/frr/ospfd.conf:`
+
+ .. code-block:: frr
- /etc/snmp/snmpd.conf:
- #
- # example access restrictions setup
- #
- com2sec readonly default public
- group MyROGroup v1 readonly
- view all included .1 80
- access MyROGroup "" any noauth exact all none none
- #
- # enable master agent for AgentX subagents
- #
- master agentx
-
- /etc/frr/ospfd.conf:
! ... the rest of ospfd.conf has been omitted for clarity ...
!
agentx
::
- 2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected
+ 2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected
Then, you can use the following command to check everything works as expected:
::
- # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1
- OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109
- [...]
+ # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1
+ OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109
+ [...]
The AgentX protocol can be transported over a Unix socket or using TCP or UDP.
::
- /etc/snmp/frr.conf:
- [snmpd]
- # Use a remote master agent
- agentXSocket tcp:192.168.15.12:705
+ [snmpd]
+ # Use a remote master agent
+ agentXSocket tcp:192.168.15.12:705
.. _smux-configuration:
using the password "frr_ospfd". For testing it is recommending to take exactly
the below snmpd.conf as wrong access restrictions can be hard to debug.
-::
-
- /etc/snmp/snmpd.conf:
- #
- # example access restrictions setup
- #
- com2sec readonly default public
- group MyROGroup v1 readonly
- view all included .1 80
- access MyROGroup "" any noauth exact all none none
- #
- # the following line is relevant for FRR
- #
- smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
-
- /etc/frr/ospf:
- ! ... the rest of ospfd.conf has been omitted for clarity ...
- !
- smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
- !
+:file:`/etc/snmp/snmpd.conf`:
+ #
+ # example access restrictions setup
+ #
+ com2sec readonly default public
+ group MyROGroup v1 readonly
+ view all included .1 80
+ access MyROGroup "" any noauth exact all none none
+ #
+ # the following line is relevant for FRR
+ #
+ smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
+
+:file:`/etc/frr/ospf`:
+ ! ... the rest of ospfd.conf has been omitted for clarity ...
+ !
+ smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
+ !
After restarting snmpd and frr, a successful connection can be verified in the
below. You can of course do all sorts of things when handling traps, like sound
a siren, have your display flash, etc., be creative ;).
-::
+.. code-block:: shell
#!/bin/bash
Enter VNC configuration mode for specifying VNC default behaviors. Use
`exit-vnc` to leave VNC configuration mode. `vnc defaults` is optional.
-::
+.. code-block:: frr
vnc defaults
... various VNC defaults
Enter VNC configuration mode for defining the NVE group `name`.
Use `exit` or `exit-vnc` to exit group configuration mode.
- ::
+ .. code-block:: frr
vnc nve-group group1
... configuration commands
Enter VNC configuration mode for defining the L2 group `name`.
Use `exit` or `exit-vnc` to exit group configuration mode.
- ::
+ .. code-block:: frr
vnc l2-group group1
... configuration commands
A three-way full mesh with three NVEs per NVA.
-:file:`bgpd.conf` for ``NVA 1`` (192.168.1.100):::
+:file:`bgpd.conf` for ``NVA 1`` (192.168.1.100):
+
+.. code-block:: frr
router bgp 64512
exit
-:file:`bgpd.conf` for ``NVA 2`` (192.168.1.101):::
+:file:`bgpd.conf` for ``NVA 2`` (192.168.1.101):
+
+.. code-block:: frr
router bgp 64512
exit-vnc
exit
-:file:`bgpd.conf` for ``NVA 3`` (192.168.1.102):::
+:file:`bgpd.conf` for ``NVA 3`` (192.168.1.102):
+
+.. code-block:: frr
router bgp 64512
connected (registering) NVE. Exporting VNC routes as IPv4 unicast is enabled
with the ``vnc export`` command below.
-The configuration for ``VNC-GW 1`` is shown below.::
+The configuration for ``VNC-GW 1`` is shown below.
+
+.. code-block:: frr
router bgp 64512
bgp router-id 192.168.1.101
by default and this prevents the other VNC-GW and NVAs from learning unicast
routes advertised by the route-reflector clients.
-Configuration for ``NVA 2``:::
+Configuration for ``NVA 2``:
+
+.. code-block:: frr
router bgp 64512
bgp router-id 192.168.1.104
7``, and ``NVE 8`` are members of the NVE group ``group1``. The NVEs ``NVE
5``, ``NVE 6``, and ``NVE 9`` are members of the NVE group ``group2``.
-:file:`bgpd.conf` for ``BGP Route Reflector 1`` on 192.168.1.100:::
+:file:`bgpd.conf` for ``BGP Route Reflector 1`` on 192.168.1.100:
+
+.. code-block:: frr
router bgp 64512
exit
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:
+
+.. code-block:: frr
router bgp 64512
exit-vnc
exit
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.102:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.102:
- router bgp 64512
+.. code-block:: frr
- bgp router-id 192.168.1.102
+ router bgp 64512
- neighbor 192.168.1.100 remote-as 64512
+ bgp router-id 192.168.1.102
- address-family ipv4 vpn
- neighbor 192.168.1.100 activate
- exit-address-family
+ neighbor 192.168.1.100 remote-as 64512
- vnc defaults
- rd 64512:1
- response-lifetime 200
- rt both 1000:1 1000:2
- exit-vnc
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
- vnc nve-group group1
- prefix vn 172.16.128.0/17
- exit-vnc
- exit
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+ exit
While not shown, an NVA can also be configured as a route reflector.
}
}
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:
+
+.. code-block:: frr
router bgp 64512
exit-vnc
exit
-:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:::
+:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:
+
+.. code-block:: frr
router bgp 64512
FRR-based NVA with redundant route reflectors
-:file:`bgpd.conf` for ``Bgpd Route Reflector 1`` on 192.168.1.100:::
+:file:`bgpd.conf` for ``Bgpd Route Reflector 1`` on 192.168.1.100:
+
+.. code-block:: frr
router bgp 64512
exit-address-family
exit
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:
- router bgp 64512
+.. code-block:: frr
- bgp router-id 192.168.1.101
+ router bgp 64512
- neighbor 192.168.1.100 remote-as 64512
- neighbor 192.168.1.104 remote-as 64512
+ bgp router-id 192.168.1.101
- address-family ipv4 vpn
- neighbor 192.168.1.100 activate
- neighbor 192.168.1.104 activate
- exit-address-family
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
- vnc nve-group group1
- prefix vn 172.16.0.0/17
- rd 64512:1
- response-lifetime 200
- rt both 1000:1 1000:2
- exit-vnc
- exit
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+
+:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:
-:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:::
+.. code-block:: frr
router bgp 64512
}
}
-.. [#] The nve-id is carriedin the route distinguisher. It is the second octet
+.. [#] The nve-id is carried in the route distinguisher. It is the second octet
of the eight-octet route distinguisher generated for Ethernet / L2
advertisements. The first octet is a constant 0xFF, and the third
through eighth octets are set to the L2
.. program:: zebra
-.. option:: -b
-.. option:: --batch
+.. option:: -b, --batch
Runs in batch mode. *zebra* parses configuration file and terminates
immediately.
-.. option:: -k
-.. option:: --keep_kernel
+.. option:: -k, --keep_kernel
When zebra starts up, don't delete old self inserted routes.
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When program terminates, retain routes added by zebra.
Some example configuration:
- ::
+ .. code-block:: frr
+
ip route 10.0.0.0/8 10.0.0.2
ip route 10.0.0.0/8 ppp0
ip route 10.0.0.0/8 null0
A.B.C.D format, user must define NETMASK value with A.B.C.D
format. GATEWAY is same option as above command.
- ::
+ .. code-block:: frr
ip route 10.0.0.0 255.255.255.0 10.0.0.2
ip route 10.0.0.0 255.255.255.0 ppp0
Installs the route with the specified distance.
-Multiple nexthop static route
+Multiple nexthop static route:
-::
+.. code-block:: frr
ip route 10.0.0.1/32 10.0.0.2
ip route 10.0.0.1/32 10.0.0.3
* is directly connected, eth0
-::
+.. code-block:: frr
ip route 10.0.0.0/8 10.0.0.2
ip route 10.0.0.0/8 10.0.0.3
MODE sets the method used to perform RPF lookups. Supported modes:
-urib-only
- Performs the lookup on the Unicast RIB. The Multicast RIB is never used.
+ urib-only
+ Performs the lookup on the Unicast RIB. The Multicast RIB is never used.
-mrib-only
- Performs the lookup on the Multicast RIB. The Unicast RIB is never used.
+ mrib-only
+ Performs the lookup on the Multicast RIB. The Unicast RIB is never used.
-mrib-then-urib
- Tries to perform the lookup on the Multicast RIB. If any route is found,
- that route is used. Otherwise, the Unicast RIB is tried.
+ mrib-then-urib
+ Tries to perform the lookup on the Multicast RIB. If any route is found,
+ that route is used. Otherwise, the Unicast RIB is tried.
-lower-distance
- Performs a lookup on the Multicast RIB and Unicast RIB each. The result
- with the lower administrative distance is used; if they're equal, the
- Multicast RIB takes precedence.
+ lower-distance
+ Performs a lookup on the Multicast RIB and Unicast RIB each. The result
+ with the lower administrative distance is used; if they're equal, the
+ Multicast RIB takes precedence.
-longer-prefix
- Performs a lookup on the Multicast RIB and Unicast RIB each. The result
- with the longer prefix length is used; if they're equal, the
- Multicast RIB takes precedence.
+ longer-prefix
+ Performs a lookup on the Multicast RIB and Unicast RIB each. The result
+ with the longer prefix length is used; if they're equal, the
+ Multicast RIB takes precedence.
- The `mrib-then-urib` setting is the default behavior if nothing is
- configured. If this is the desired behavior, it should be explicitly
- configured to make the configuration immune against possible changes in
- what the default behavior is.
+ The `mrib-then-urib` setting is the default behavior if nothing is
+ configured. If this is the desired behavior, it should be explicitly
+ configured to make the configuration immune against possible changes in
+ what the default behavior is.
.. warning::
Unreachable routes do not receive special treatment and do not cause
that sets the preferred source address, and applies the route-map to all
*rip* routes.
-::
+.. code-block:: frr
ip prefix-list ANY permit 0.0.0.0/0 le 32
route-map RM1 permit 10
RUN apk add --no-cache abuild alpine-sdk && mkdir -p /pkgs/apk
ADD docker/alpine/alpine-build.sh /usr/bin/
ADD docker/alpine/builder /etc/sudoers.d
-COPY --from=source-builder /src/*.tar.gz /src/alpine/APKBUILD /dist/
+COPY --from=source-builder /src/*.tar.gz /src/alpine/* /src/tools/etc/frr/daemons* /dist/
RUN adduser -D -G abuild builder && chown -R builder /dist /pkgs
USER builder
RUN /usr/bin/alpine-build.sh
FROM alpine:3.7
RUN mkdir -p /pkgs/apk
COPY --from=alpine-builder /pkgs/apk/ /pkgs/apk/
+RUN apk add --no-cache tini
RUN apk add --no-cache --allow-untrusted /pkgs/apk/x86_64/*.apk
+ENTRYPOINT [ "/sbin/tini", "--", "/usr/lib/frr/docker-start" ]
struct eigrp_neighbor *nbr;
struct route_node *rn;
struct eigrp_neighbor *min = NULL;
- struct eigrp *eigrp = eigrp;
+ struct eigrp *eigrp;
eigrp = eigrp_lookup();
size_t iovcnt = 0;
size_t nbyte = 0;
+ if (fd < 0)
+ return BUFFER_ERROR;
+
for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
d = d->next, iovcnt++) {
iov[iovcnt].iov_base = d->data + d->sp;
*/
%{
+/* ignore flex generated code in static analyzer */
+#ifndef __clang_analyzer__
+
/* ignore harmless bugs in old versions of flex */
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
// yy_delete_buffer (buffer, *scn);
yylex_destroy(*scn);
}
+
+#endif /* __clang_analyzer__ */
struct listnode *head = listhead(*argv);
struct listnode *tail = listtail(*argv);
+ assert(head);
+ assert(tail);
+
// delete dummy start node
cmd_token_del((struct cmd_token *)head->data);
list_delete_node(*argv, head);
struct ibuf *wbuf;
struct imsg_hdr hdr;
+ memset(&hdr, 0x00, IMSG_HEADER_SIZE);
+
datalen += IMSG_HEADER_SIZE;
if (datalen > MAX_IMSGSIZE) {
errno = ERANGE;
snprintf(p_instance, sizeof(p_instance), "-%d", di->instance);
}
if (di->pathspace)
- snprintf(p_pathspace, sizeof(p_pathspace), "/%s",
+ snprintf(p_pathspace, sizeof(p_pathspace), "%s/",
di->pathspace);
snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf",
frr_sysconfdir, p_pathspace, di->name, p_instance);
- snprintf(pidfile_default, sizeof(pidfile_default), "%s%s/%s%s.pid",
+ snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid",
frr_vtydir, p_pathspace, di->name, p_instance);
zprivs_preinit(di->privs);
DESC_ENTRY(ZEBRA_TABLE_MANAGER_CONNECT),
DESC_ENTRY(ZEBRA_GET_TABLE_CHUNK),
DESC_ENTRY(ZEBRA_RELEASE_TABLE_CHUNK),
+ DESC_ENTRY(ZEBRA_IPSET_CREATE),
+ DESC_ENTRY(ZEBRA_IPSET_DESTROY),
+ DESC_ENTRY(ZEBRA_IPSET_ENTRY_ADD),
+ DESC_ENTRY(ZEBRA_IPSET_ENTRY_DELETE),
};
#undef DESC_ENTRY
--- /dev/null
+/*
+ * mpls functions
+ *
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This program 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 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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 <mpls.h>
+#include <memory.h>
+
+/*
+ * String to label conversion, labels separated by '/'.
+ *
+ * @param label_str labels separated by /
+ * @param num_labels number of labels; zero if conversion was unsuccessful
+ * @param labels preallocated mpls_label_t array of size MPLS_MAX_LABELS; only
+ * modified if the conversion succeeded
+ * @return 0 on success
+ * -1 if the string could not be parsed as integers
+ * -2 if a label was inside the reserved range (0-15)
+ * -3 if the number of labels given exceeds MPLS_MAX_LABELS
+ */
+int mpls_str2label(const char *label_str, uint8_t *num_labels,
+ mpls_label_t *labels)
+{
+ char *ostr; // copy of label string (start)
+ char *lstr; // copy of label string
+ char *nump; // pointer to next segment
+ char *endp; // end pointer
+ int i; // for iterating label_str
+ int rc; // return code
+ mpls_label_t pl[MPLS_MAX_LABELS]; // parsed labels
+
+ /* labels to zero until we have a successful parse */
+ ostr = lstr = XSTRDUP(MTYPE_TMP, label_str);
+ *num_labels = 0;
+ rc = 0;
+
+ for (i = 0; i < MPLS_MAX_LABELS && lstr && !rc; i++) {
+ nump = strsep(&lstr, "/");
+ pl[i] = strtoul(nump, &endp, 10);
+
+ /* format check */
+ if (*endp != '\0')
+ rc = -1;
+ /* validity check */
+ else if (!IS_MPLS_UNRESERVED_LABEL(pl[i]))
+ rc = -2;
+ }
+
+ /* excess labels */
+ if (!rc && i == MPLS_MAX_LABELS && lstr)
+ rc = -3;
+
+ if (!rc) {
+ *num_labels = i;
+ memcpy(labels, pl, *num_labels * sizeof(mpls_label_t));
+ }
+
+ XFREE(MTYPE_TMP, ostr);
+
+ return rc;
+}
+
+/*
+ * Label to string conversion, labels in string separated by '/'.
+ */
+char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf,
+ int len, int pretty)
+{
+ char label_buf[BUFSIZ];
+ int i;
+
+ buf[0] = '\0';
+ for (i = 0; i < num_labels; i++) {
+ if (i != 0)
+ strlcat(buf, "/", len);
+ if (pretty)
+ label2str(labels[i], label_buf, sizeof(label_buf));
+ else
+ snprintf(label_buf, sizeof(label_buf), "%u", labels[i]);
+ strlcat(buf, label_buf, len);
+ }
+
+ return buf;
+}
#undef MPLS_LABEL_MAX
#endif
+#define MPLS_LABEL_HELPSTR \
+ "Specify label(s) for this route\nOne or more " \
+ "labels in the range (16-1048575) separated by '/'\n"
+
/* Well-known MPLS label values (RFC 3032 etc). */
#define MPLS_LABEL_IPV4_EXPLICIT_NULL 0 /* [RFC3032] */
#define MPLS_LABEL_ROUTER_ALERT 1 /* [RFC3032] */
}
}
+/*
+ * String to label conversion, labels separated by '/'.
+ */
+int mpls_str2label(const char *label_str, uint8_t *num_labels,
+ mpls_label_t *labels);
+
+/*
+ * Label to string conversion, labels in string separated by '/'.
+ */
+char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf,
+ int len, int pretty);
#endif
static int ns_debug;
+struct ns_map_nsid {
+ RB_ENTRY(ns_map_nsid) id_entry;
+ ns_id_t ns_id_external;
+ ns_id_t ns_id;
+};
+
+static inline int ns_map_compare(const struct ns_map_nsid *a,
+ const struct ns_map_nsid *b)
+{
+ return (a->ns_id - b->ns_id);
+}
+
+RB_HEAD(ns_map_nsid_head, ns_map_nsid);
+RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
+RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
+struct ns_map_nsid_head ns_map_nsid_list = RB_INITIALIZER(&ns_map_nsid_list);
+
+static ns_id_t ns_id_external_numbering;
+
+
#ifndef CLONE_NEWNET
#define CLONE_NEWNET 0x40000000
/* New network namespace (lo, device, names sockets, etc) */
}
}
+/* VRF list existance check by name. */
+static struct ns_map_nsid *ns_map_nsid_lookup_by_nsid(ns_id_t ns_id)
+{
+ struct ns_map_nsid ns_map;
+
+ ns_map.ns_id = ns_id;
+ return RB_FIND(ns_map_nsid_head, &ns_map_nsid_list, &ns_map);
+}
+
+ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map)
+{
+ struct ns_map_nsid *ns_map;
+ vrf_id_t ns_id_external;
+
+ ns_map = ns_map_nsid_lookup_by_nsid(ns_id);
+ if (ns_map && !map) {
+ ns_id_external = ns_map->ns_id_external;
+ RB_REMOVE(ns_map_nsid_head, &ns_map_nsid_list, ns_map);
+ return ns_id_external;
+ }
+ if (ns_map)
+ return ns_map->ns_id_external;
+ ns_map = XCALLOC(MTYPE_NS, sizeof(struct ns_map_nsid));
+ /* increase vrf_id
+ * default vrf is the first one : 0
+ */
+ ns_map->ns_id_external = ns_id_external_numbering++;
+ ns_map->ns_id = ns_id;
+ RB_INSERT(ns_map_nsid_head, &ns_map_nsid_list, ns_map);
+ return ns_map->ns_id_external;
+}
+
struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
{
return ns_get_created_internal(ns, name, ns_id);
}
/* Initialize NS module. */
-void ns_init_management(ns_id_t default_ns_id)
+void ns_init_management(ns_id_t default_ns_id, ns_id_t internal_ns)
{
int fd;
fd = open(NS_DEFAULT_NAME, O_RDONLY);
default_ns->fd = fd;
}
+ default_ns->internal_ns_id = internal_ns;
+
/* Set the default NS name. */
default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
if (ns_debug)
return 0;
}
+ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool maporunmap)
+{
+ return NS_UNKNOWN;
+}
+
struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
{
return NULL;
#include <zebra.h>
#include <vrf.h>
+#include <sockunion.h>
#include <nexthop.h>
#include <nexthop_group.h>
#include <vty.h>
if (nexthop->next)
nexthop->next->prev = nexthop->prev;
+
+ nh->prev = NULL;
+ nh->next = NULL;
}
void copy_nexthops(struct nexthop **tnh, struct nexthop *nh,
while (nexthop) {
struct nexthop *next = nexthop_next(nexthop);
+ nexthop_del(&nhgc->nhg, nexthop);
if (nhg_hooks.del_nexthop)
nhg_hooks.del_nexthop(nhgc, nexthop);
return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
}
+static int nhgc_cmp_helper(const char *a, const char *b)
+{
+ if (!a && !b)
+ return 0;
+
+ if (a && !b)
+ return -1;
+
+ if (!a && b)
+ return 1;
+
+ return strcmp(a, b);
+}
+
+static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
+{
+ int ret;
+
+ ret = sockunion_cmp(&nh1->addr, &nh2->addr);
+ if (ret)
+ return ret;
+
+ ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
+ if (ret)
+ return ret;
+
+ return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
+}
+
+static void nhgl_delete(struct nexthop_hold *nh)
+{
+ if (nh->intf)
+ XFREE(MTYPE_TMP, nh->intf);
+
+ if (nh->nhvrf_name)
+ XFREE(MTYPE_TMP, nh->nhvrf_name);
+
+ XFREE(MTYPE_TMP, nh);
+}
+
static struct nexthop_group_cmd *nhgc_get(const char *name)
{
struct nexthop_group_cmd *nhgc;
QOBJ_REG(nhgc, nexthop_group_cmd);
RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
+ nhgc->nhg_list = list_new();
+ nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
+ nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
+
if (nhg_hooks.new)
nhg_hooks.new(name);
}
nhg_hooks.delete(nhgc->name);
RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
+
+ list_delete_and_null(&nhgc->nhg_list);
+
+ XFREE(MTYPE_TMP, nhgc);
}
DEFINE_QOBJ_TYPE(nexthop_group_cmd)
return CMD_SUCCESS;
}
-DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
- "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
- NO_STR
- "Specify one of the nexthops in this ECMP group\n"
- "v4 Address\n"
- "v6 Address\n"
- "Interface to use\n"
- "If the nexthop is in a different vrf tell us\n"
- "The nexthop-vrf Name\n")
+static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
+ const char *nhvrf_name,
+ const union sockunion *addr,
+ const char *intf)
+{
+ struct nexthop_hold *nh;
+
+ nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
+
+ if (nhvrf_name)
+ nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
+ if (intf)
+ nh->intf = XSTRDUP(MTYPE_TMP, intf);
+
+ nh->addr = *addr;
+
+ listnode_add_sort(nhgc->nhg_list, nh);
+}
+
+static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
+ const char *nhvrf_name,
+ const union sockunion *addr,
+ const char *intf)
+{
+ struct nexthop_hold *nh;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
+ if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
+ sockunion_cmp(addr, &nh->addr) == 0 &&
+ nhgc_cmp_helper(intf, nh->intf) == 0)
+ break;
+ }
+
+ /*
+ * Something has gone seriously wrong, fail gracefully
+ */
+ if (!nh)
+ return;
+
+ list_delete_node(nhgc->nhg_list, node);
+
+ if (nh->nhvrf_name)
+ XFREE(MTYPE_TMP, nh->nhvrf_name);
+ if (nh->intf)
+ XFREE(MTYPE_TMP, nh->intf);
+
+ XFREE(MTYPE_TMP, nh);
+}
+
+static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
+ const union sockunion *addr,
+ const char *intf, const char *name)
{
- VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
struct vrf *vrf;
- struct nexthop nhop;
- struct nexthop *nh;
+
+ memset(nhop, 0, sizeof(*nhop));
if (name)
vrf = vrf_lookup_by_name(name);
else
vrf = vrf_lookup_by_id(VRF_DEFAULT);
- if (!vrf) {
- vty_out(vty, "Specified: %s is non-existent\n", name);
- return CMD_WARNING;
- }
+ if (!vrf)
+ return false;
- memset(&nhop, 0, sizeof(nhop));
- nhop.vrf_id = vrf->vrf_id;
+ nhop->vrf_id = vrf->vrf_id;
if (addr->sa.sa_family == AF_INET) {
- nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
+ nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
if (intf) {
- nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX;
- nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop.ifindex == IFINDEX_INTERNAL) {
- vty_out(vty,
- "Specified Intf %s does not exist in vrf: %s\n",
- intf, vrf->name);
- return CMD_WARNING;
- }
+ nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
+ if (nhop->ifindex == IFINDEX_INTERNAL)
+ return false;
} else
- nhop.type = NEXTHOP_TYPE_IPV4;
+ nhop->type = NEXTHOP_TYPE_IPV4;
} else {
- memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16);
+ memcpy(&nhop->gate.ipv6, &addr->sin6.sin6_addr, 16);
if (intf) {
- nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
- nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop.ifindex == IFINDEX_INTERNAL) {
- vty_out(vty,
- "Specified Intf %s does not exist in vrf: %s\n",
- intf, vrf->name);
- return CMD_WARNING;
- }
+ nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
+ if (nhop->ifindex == IFINDEX_INTERNAL)
+ return false;
} else
- nhop.type = NEXTHOP_TYPE_IPV6;
+ nhop->type = NEXTHOP_TYPE_IPV6;
+ }
+
+ return true;
+}
+
+DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
+ "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
+ NO_STR
+ "Specify one of the nexthops in this ECMP group\n"
+ "v4 Address\n"
+ "v6 Address\n"
+ "Interface to use\n"
+ "If the nexthop is in a different vrf tell us\n"
+ "The nexthop-vrf Name\n")
+{
+ VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
+ struct nexthop nhop;
+ struct nexthop *nh;
+ bool legal;
+
+ legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name);
+
+ if (nhop.type == NEXTHOP_TYPE_IPV6
+ && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
+ vty_out(vty,
+ "Specified a v6 LL with no interface, rejecting\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
nh = nexthop_exists(&nhgc->nhg, &nhop);
if (no) {
+ nexthop_group_unsave_nhop(nhgc, name, addr, intf);
if (nh) {
nexthop_del(&nhgc->nhg, nh);
}
} else if (!nh) {
/* must be adding new nexthop since !no and !nexthop_exists */
- nh = nexthop_new();
+ if (legal) {
+ nh = nexthop_new();
+
+ memcpy(nh, &nhop, sizeof(nhop));
+ nexthop_add(&nhgc->nhg.nexthop, nh);
+ }
- memcpy(nh, &nhop, sizeof(nhop));
- nexthop_add(&nhgc->nhg.nexthop, nh);
+ nexthop_group_save_nhop(nhgc, name, addr, intf);
- if (nhg_hooks.add_nexthop)
+ if (legal && nhg_hooks.add_nexthop)
nhg_hooks.add_nexthop(nhgc, nh);
}
vty_out(vty, "\n");
}
+static void nexthop_group_write_nexthop_internal(struct vty *vty,
+ struct nexthop_hold *nh)
+{
+ char buf[100];
+
+ vty_out(vty, "nexthop ");
+
+ vty_out(vty, "%s", sockunion2str(&nh->addr, buf, sizeof(buf)));
+
+ if (nh->intf)
+ vty_out(vty, " %s", nh->intf);
+
+ if (nh->nhvrf_name)
+ vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
+
+ vty_out(vty, "\n");
+}
+
static int nexthop_group_write(struct vty *vty)
{
struct nexthop_group_cmd *nhgc;
- struct nexthop *nh;
+ struct nexthop_hold *nh;
RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+
vty_out(vty, "nexthop-group %s\n", nhgc->name);
- for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) {
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
vty_out(vty, " ");
- nexthop_group_write_nexthop(vty, nh);
+ nexthop_group_write_nexthop_internal(vty, nh);
}
vty_out(vty, "!\n");
return 1;
}
+void nexthop_group_enable_vrf(struct vrf *vrf)
+{
+ struct nexthop_group_cmd *nhgc;
+ struct nexthop_hold *nhh;
+
+ RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
+ struct nexthop nhop;
+ struct nexthop *nh;
+
+ if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
+ nhh->intf,
+ nhh->nhvrf_name))
+ continue;
+
+ nh = nexthop_exists(&nhgc->nhg, &nhop);
+
+ if (nh)
+ continue;
+
+ if (nhop.vrf_id != vrf->vrf_id)
+ continue;
+
+ nh = nexthop_new();
+
+ memcpy(nh, &nhop, sizeof(nhop));
+ nexthop_add(&nhgc->nhg.nexthop, nh);
+
+ if (nhg_hooks.add_nexthop)
+ nhg_hooks.add_nexthop(nhgc, nh);
+ }
+ }
+}
+
+void nexthop_group_disable_vrf(struct vrf *vrf)
+{
+ struct nexthop_group_cmd *nhgc;
+ struct nexthop_hold *nhh;
+
+ RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
+ struct nexthop nhop;
+ struct nexthop *nh;
+
+ if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
+ nhh->intf,
+ nhh->nhvrf_name))
+ continue;
+
+ nh = nexthop_exists(&nhgc->nhg, &nhop);
+
+ if (!nh)
+ continue;
+
+ if (nh->vrf_id != vrf->vrf_id)
+ continue;
+
+ nexthop_del(&nhgc->nhg, nh);
+
+ if (nhg_hooks.del_nexthop)
+ nhg_hooks.del_nexthop(nhgc, nh);
+
+ nexthop_free(nh);
+ }
+ }
+}
+
+void nexthop_group_interface_state_change(struct interface *ifp,
+ ifindex_t oldifindex)
+{
+ struct nexthop_group_cmd *nhgc;
+ struct nexthop_hold *nhh;
+
+ RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+ struct nexthop *nh;
+
+ if (if_is_up(ifp)) {
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
+ struct nexthop nhop;
+
+ if (!nexthop_group_parse_nexthop(
+ &nhop, &nhh->addr, nhh->intf,
+ nhh->nhvrf_name))
+ continue;
+
+ switch (nhop.type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ continue;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ break;
+ }
+ nh = nexthop_exists(&nhgc->nhg, &nhop);
+
+ if (nh)
+ continue;
+
+ if (ifp->ifindex != nhop.ifindex)
+ continue;
+
+ nh = nexthop_new();
+
+ memcpy(nh, &nhop, sizeof(nhop));
+ nexthop_add(&nhgc->nhg.nexthop, nh);
+
+ if (nhg_hooks.add_nexthop)
+ nhg_hooks.add_nexthop(nhgc, nh);
+ }
+ } else {
+ struct nexthop *next_nh;
+
+ for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
+ next_nh = nh->next;
+ switch (nh->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ continue;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ break;
+ }
+
+ if (oldifindex != nh->ifindex)
+ continue;
+
+ nexthop_del(&nhgc->nhg, nh);
+
+ if (nhg_hooks.del_nexthop)
+ nhg_hooks.del_nexthop(nhgc, nh);
+
+ nexthop_free(nh);
+ }
+ }
+ }
+}
+
void nexthop_group_init(void (*new)(const char *name),
void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
const struct nexthop *nhop),
(nhop); \
(nhop) = nexthop_next(nhop)
+
+struct nexthop_hold {
+ char *nhvrf_name;
+ union sockunion addr;
+ char *intf;
+};
+
struct nexthop_group_cmd {
RB_ENTRY(nexthop_group_cmd) nhgc_entry;
struct nexthop_group nhg;
+ struct list *nhg_list;
+
QOBJ_FIELDS
};
RB_HEAD(nhgc_entry_head, nexthp_group_cmd);
const struct nexthop *nhop),
void (*delete)(const char *name));
+void nexthop_group_enable_vrf(struct vrf *vrf);
+void nexthop_group_disable_vrf(struct vrf *vrf);
+void nexthop_group_interface_state_change(struct interface *ifp,
+ ifindex_t oldifindex);
+
extern struct nexthop *nexthop_exists(struct nexthop_group *nhg,
struct nexthop *nh);
/* Identifier, same as the vector index */
ns_id_t ns_id;
+ /* Identifier, mapped on the NSID value */
+ ns_id_t internal_ns_id;
+
/* Name */
char *name;
/* API to initialize NETNS managerment
* parameter is the default ns_id
*/
-extern void ns_init_management(ns_id_t ns_id);
+extern void ns_init_management(ns_id_t ns_id, ns_id_t internal_ns_idx);
/*
/* API to get context information of a NS */
extern void *ns_info_lookup(ns_id_t ns_id);
+/* API to map internal ns id value with
+ * user friendly ns id external value
+ */
+extern ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map);
+
/*
* NS init routine
* should be called from backendx
--- /dev/null
+/* Policy Based Routing (PBR) main header
+ * Copyright (C) 2018 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 FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _PBR_H
+#define _PBR_H
+
+/*
+ * A PBR filter
+ *
+ * The filter or match criteria in a PBR rule.
+ * For simplicity, all supported filters are grouped into a structure rather
+ * than delineating further. A bitmask denotes which filters are actually
+ * specified.
+ */
+struct pbr_filter {
+ uint32_t filter_bm; /* not encoded by zapi
+ */
+#define PBR_FILTER_SRC_IP (1 << 0)
+#define PBR_FILTER_DST_IP (1 << 1)
+#define PBR_FILTER_SRC_PORT (1 << 2)
+#define PBR_FILTER_DST_PORT (1 << 3)
+#define PBR_FILTER_FWMARK (1 << 4)
+
+ /* Source and Destination IP address with masks. */
+ struct prefix src_ip;
+ struct prefix dst_ip;
+
+ /* Source and Destination higher-layer (TCP/UDP) port numbers. */
+ uint16_t src_port;
+ uint16_t dst_port;
+
+ /* Filter with fwmark */
+ uint32_t fwmark;
+};
+
+/*
+ * A PBR action
+ *
+ * The action corresponding to a PBR rule.
+ * While the user specifies the action in a particular way, the forwarding
+ * plane implementation (Linux only) requires that to be encoded into a
+ * route table and the rule then point to that route table; in some cases,
+ * the user criteria may directly point to a table too.
+ */
+struct pbr_action {
+ uint32_t table;
+};
+
+/*
+ * A PBR rule
+ *
+ * This is a combination of the filter criteria and corresponding action.
+ * Rules also have a user-defined sequence number which defines the relative
+ * order amongst rules.
+ */
+struct pbr_rule {
+ vrf_id_t vrf_id;
+
+ uint32_t seq;
+ uint32_t priority;
+ uint32_t unique;
+ struct pbr_filter filter;
+ struct pbr_action action;
+ uint32_t ifindex;
+};
+
+extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
+ struct pbr_rule *zrule);
+
+#endif /* _PBR_H */
$_ =~ s/\s*,\s*/,/g;
- # else: 7-field line
+ # else: 8-field line
my @f = split(/,/, $_);
- unless (@f == 7 || @f == 8) {
+ unless (@f == 8 || @f == 9) {
die "invalid input on route_types line $.\n";
}
my $proto = $f[0];
$f[3] = $1 if ($f[3] =~ /^'(.*)'$/);
- $f[6] = $1 if ($f[6] =~ /^"(.*)"$/);
+ $f[7] = $1 if ($f[7] =~ /^"(.*)"$/);
$protodetail{$proto} = {
"number" => scalar @protos,
"char" => $f[3],
"ipv4" => int($f[4]),
"ipv6" => int($f[5]),
- "shorthelp" => $f[6],
- "restrict2" => $f[7],
+ "redist" => int($f[6]),
+ "shorthelp" => $f[7],
+ "restrict2" => $f[8],
};
push @protos, $proto;
$daemons{$f[2]} = {
next if ($protodetail{$p}->{"daemon"} eq $daemon && $daemon ne "zebra");
next if ($protodetail{$p}->{"restrict2"} ne "" &&
$protodetail{$p}->{"restrict2"} ne $daemon);
+ next if ($protodetail{$p}->{"redist"} eq 0);
next unless (($ipv4 && $protodetail{$p}->{"ipv4"})
- || ($ipv6 && $protodetail{$p}->{"ipv6"}));
+ || ($ipv6 && $protodetail{$p}->{"ipv6"}));
push @names, $protodetail{$p}->{"cname"};
push @help, " \"".$protodetail{$p}->{"longhelp"}."\\n\"";
}
# Lines /beginning/ with # are comments.
#
####
-# 7 field line has format:
-# ZServ route type, canonical name, daemon, route char, ipv4, ipv6, short desc
+# 9 field line has format:
+# ZServ route type, canonical name, daemon, route char, ipv4, ipv6, redist, short desc, Restrictions
#
# Zserv route type: Corresponding with zebra.h. Key field.
# canonical name: Typically derived from the route type definition.
# 'X' is reserved as the 'not needed' placeholder.
# ipv4: IPv4 capable? yes/no, or 1/0.
# ipv6: IPv6 capable? ditto.
+# redist: Allow this protocol to be used in redistribution statements
# short desc: Very brief description. Used in header of
# 'show ip route'. May be specified as NULL
# if the canonical name suffices.
+# Restriction: If this cannot be used with the listed protocol for redistribution events
#
# Key fields obviously must be a unique ASCII alpha-numeric word.
# Lower-case is required, brevity is optional but highly desirable.
# If you add a new routing protocol here, make sure you also update
# meta_queue_map in zebra_rib.c
#
-## type cname daemon C 4 6 short help
-ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, "Reserved"
-ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, "kernel route"
-ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, "connected"
-ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, "static"
-ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, "RIP"
-ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, "RIPng"
-ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF"
-ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv3"
-ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS"
-ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP"
-ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM"
-ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, "EIGRP"
-ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP"
+## type cname daemon C 4 6 Redist short help Restrictions
+ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, 0, "Reserved"
+ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, 1, "kernel route"
+ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, 1, "connected"
+ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, 1, "static"
+ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, 1, "RIP"
+ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, 1, "RIPng"
+ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, 1, "OSPF"
+ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, 1, "OSPFv3"
+ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, 1, "IS-IS"
+ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, 1, "BGP"
+ZEBRA_ROUTE_PIM, pim, pimd, 'P', 0, 0, 0, "PIM"
+ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, 1, "EIGRP"
+ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, 1, "NHRP"
# HSLS and OLSR both are AFI independent (so: 1, 1), however
# we want to disable for them for general Quagga distribution.
# This at least makes it trivial for users of these protocols
# to 'switch on' redist support (direct numeric entry remaining
# possible).
-ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS"
-ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR"
-ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, "Table"
-ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, "LDP"
+ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, 0, "HSLS"
+ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, 0, "OLSR"
+ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, 1, "Table"
+ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, 0, "LDP"
#vnc when sent to zebra
-ZEBRA_ROUTE_VNC, vnc, NULL, 'v', 1, 1, "VNC"
+ZEBRA_ROUTE_VNC, vnc, NULL, 'v', 1, 1, 1, "VNC"
# vnc when sent to bgp
-ZEBRA_ROUTE_VNC_DIRECT, vnc-direct,NULL, 'V', 1, 1, "VNC-Direct", bgpd
+ZEBRA_ROUTE_VNC_DIRECT, vnc-direct,NULL, 'V', 1, 1, 1, "VNC-Direct", bgpd
# vnc when sent to bgp (resolve NVE mode)
-ZEBRA_ROUTE_VNC_DIRECT_RH, vnc-rn, NULL, 'V', 0, 0, "VNC-RN"
+ZEBRA_ROUTE_VNC_DIRECT_RH, vnc-rn, NULL, 'V', 0, 0, 0, "VNC-RN"
# bgp unicast -> vnc
-ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct"
+ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, 0, "BGP-Direct"
# bgp unicast -> vnc
-ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC"
-ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel"
-ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP"
-ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, "PBR"
-ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-"
+ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, 0, "BGP2VNC"
+ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, 1, "Babel"
+ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP"
+ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR"
+ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
## help strings
lib/memory.c \
lib/memory_vty.c \
lib/module.c \
+ lib/mpls.c \
lib/network.c \
lib/nexthop.c \
lib/netns_linux.c \
lib/zclient.h \
lib/zebra.h \
lib/logicalrouter.h \
+ lib/pbr.h \
# end
nodist_pkginclude_HEADERS += \
#include "command.h"
#include "ns.h"
#include "privs.h"
+#include "nexthop_group.h"
/* default VRF ID value used when VRF backend is not NETNS */
#define VRF_DEFAULT_INTERNAL 0
if (!name && vrf_id == VRF_UNKNOWN)
return NULL;
+ /* attempt to find already available VRF
+ */
+ if (name)
+ vrf = vrf_lookup_by_name(name);
/* Try to find VRF both by ID and name */
- if (vrf_id != VRF_UNKNOWN)
+ if (!vrf && vrf_id != VRF_UNKNOWN)
vrf = vrf_lookup_by_id(vrf_id);
- if (!vrf && name)
- vrf = vrf_lookup_by_name(name);
if (vrf == NULL) {
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
if (vrf_master.vrf_enable_hook)
(*vrf_master.vrf_enable_hook)(vrf);
+ /*
+ * If we have any nexthop group entries that
+ * are awaiting vrf initialization then
+ * let's let people know about it
+ */
+ nexthop_group_enable_vrf(vrf);
+
return 1;
}
vrf_backend = vrf_backend_netns;
}
-int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf)
+int vrf_handler_create(struct vty *vty, const char *vrfname,
+ struct vrf **vrf)
{
struct vrf *vrfp;
}
int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname,
- ns_id_t ns_id)
+ ns_id_t ns_id, ns_id_t internal_ns_id)
{
struct ns *ns = NULL;
return CMD_WARNING_CONFIG_FAILED;
}
ns = ns_get_created(ns, pathname, ns_id);
+ ns->internal_ns_id = internal_ns_id;
ns->vrf_ctxt = (void *)vrf;
vrf->ns_ctxt = (void *)ns;
/* update VRF netns NAME */
vrf_daemon_privs->change(ZPRIVS_RAISE))
zlog_err("%s: Can't raise privileges", __func__);
- ret = vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
+ ret = vrf_netns_handler_create(vty, vrf, pathname,
+ NS_UNKNOWN, NS_UNKNOWN);
if (vrf_daemon_privs &&
vrf_daemon_privs->change(ZPRIVS_LOWER))
if (vrf)
return vrf->vrf_id;
+ /* backend netns is only known by zebra
+ * for other daemons, we return VRF_DEFAULT_INTERNAL
+ */
if (vrf_is_backend_netns())
return ns_get_default_id();
else
* should be called from zebra only
*/
extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
- char *pathname, ns_id_t ns_id);
+ char *pathname, ns_id_t ext_ns_id,
+ ns_id_t ns_id);
/* used internally to enable or disable VRF.
* Notify a change in the VRF ID of the VRF
#include "nexthop.h"
#include "mpls.h"
#include "sockopt.h"
+#include "pbr.h"
+#include "nexthop_group.h"
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
return 0;
}
+static void zapi_encode_prefix(struct stream *s,
+ struct prefix *p,
+ uint8_t family)
+{
+ struct prefix any;
+
+ if (!p) {
+ memset(&any, 0, sizeof(any));
+ any.family = family;
+ p = &any;
+ }
+
+ stream_putc(s, p->family);
+ stream_putc(s, p->prefixlen);
+ stream_put(s, &p->u.prefix, prefix_blen(p));
+}
+
+int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
+ struct pbr_rule *zrule)
+{
+ stream_reset(s);
+ zclient_create_header(s, cmd, zrule->vrf_id);
+
+ /*
+ * We are sending one item at a time at the moment
+ */
+ stream_putl(s, 1);
+
+ stream_putl(s, zrule->seq);
+ stream_putl(s, zrule->priority);
+ stream_putl(s, zrule->unique);
+
+ zapi_encode_prefix(s, &(zrule->filter.src_ip),
+ zrule->filter.src_ip.family);
+ stream_putw(s, zrule->filter.src_port); /* src port */
+ zapi_encode_prefix(s, &(zrule->filter.dst_ip),
+ zrule->filter.src_ip.family);
+ stream_putw(s, zrule->filter.dst_port); /* dst port */
+ stream_putw(s, zrule->filter.fwmark); /* fwmark */
+
+ stream_putl(s, zrule->action.table);
+ stream_putl(s, zrule->ifindex);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return 0;
+}
+
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
uint32_t *tableid,
enum zapi_route_notify_owner *note)
return false;
}
+bool zapi_ipset_notify_decode(struct stream *s,
+ uint32_t *unique,
+ enum zapi_ipset_notify_owner *note)
+{
+ uint32_t uni;
+
+ STREAM_GET(note, s, sizeof(*note));
+
+ STREAM_GETL(s, uni);
+
+ if (zclient_debug)
+ zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni);
+ *unique = uni;
+
+ return true;
+
+stream_failure:
+ return false;
+}
+
+bool zapi_ipset_entry_notify_decode(struct stream *s,
+ uint32_t *unique,
+ char *ipset_name,
+ enum zapi_ipset_entry_notify_owner *note)
+{
+ uint32_t uni;
+
+ STREAM_GET(note, s, sizeof(*note));
+
+ STREAM_GETL(s, uni);
+
+ STREAM_GET(ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ if (zclient_debug)
+ zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni);
+ *unique = uni;
+
+ return true;
+
+stream_failure:
+ return false;
+}
+
struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
{
struct nexthop *n = nexthop_new();
void zebra_interface_if_set_value(struct stream *s, struct interface *ifp)
{
uint8_t link_params_status = 0;
+ ifindex_t old_ifindex;
+ old_ifindex = ifp->ifindex;
/* Read interface's index. */
if_set_index(ifp, stream_getl(s));
ifp->status = stream_getc(s);
struct if_link_params *iflp = if_link_params_get(ifp);
link_params_set_value(s, iflp);
}
+
+ nexthop_group_interface_state_change(ifp, old_ifindex);
}
size_t zebra_interface_link_params_write(struct stream *s,
return (int)result;
}
+/*
+ * Asynchronous label chunk request
+ *
+ * @param zclient Zclient used to connect to label manager (zebra)
+ * @param keep Avoid garbage collection
+ * @param chunk_size Amount of labels requested
+ * @result 0 on success, -1 otherwise
+ */
+int zclient_send_get_label_chunk(
+ struct zclient *zclient,
+ uint8_t keep,
+ uint32_t chunk_size)
+{
+ struct stream *s;
+
+ if (zclient_debug)
+ zlog_debug("Getting Label Chunk");
+
+ if (zclient->sock < 0)
+ return -1;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, VRF_DEFAULT);
+ stream_putc(s, keep);
+ stream_putl(s, chunk_size);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
/**
* Function to request a label chunk in a syncronous way
*
if (zclient->rule_notify_owner)
(*zclient->rule_notify_owner)(command, zclient, length,
vrf_id);
+ break;
+ case ZEBRA_GET_LABEL_CHUNK:
+ if (zclient->label_chunk)
+ (*zclient->label_chunk)(command, zclient, length,
+ vrf_id);
+ break;
default:
break;
}
*/
#define ZAPI_TCP_PATHNAME "@tcp"
+/* IPset size name stands for the name of the ipset entry
+ * that can be created by using some zapi interfaces
+ */
+#define ZEBRA_IPSET_NAME_SIZE 32
+
+/* IPTable action is defined by two values: either
+ * forward or drop
+ */
+#define ZEBRA_IPTABLES_FORWARD 0
+#define ZEBRA_IPTABLES_DROP 1
+
extern struct sockaddr_storage zclient_addr;
extern socklen_t zclient_addr_len;
ZEBRA_TABLE_MANAGER_CONNECT,
ZEBRA_GET_TABLE_CHUNK,
ZEBRA_RELEASE_TABLE_CHUNK,
+ ZEBRA_IPSET_CREATE,
+ ZEBRA_IPSET_DESTROY,
+ ZEBRA_IPSET_ENTRY_ADD,
+ ZEBRA_IPSET_ENTRY_DELETE,
+ ZEBRA_IPSET_NOTIFY_OWNER,
+ ZEBRA_IPSET_ENTRY_NOTIFY_OWNER,
+ ZEBRA_IPTABLE_ADD,
+ ZEBRA_IPTABLE_DELETE,
+ ZEBRA_IPTABLE_NOTIFY_OWNER,
} zebra_message_types_t;
struct redist_proto {
uint16_t length, vrf_id_t vrf_id);
int (*rule_notify_owner)(int command, struct zclient *zclient,
uint16_t length, vrf_id_t vrf_id);
+ void (*label_chunk)(int command, struct zclient *zclient,
+ uint16_t length, vrf_id_t vrf_id);
+ int (*ipset_notify_owner)(int command, struct zclient *zclient,
+ uint16_t length, vrf_id_t vrf_id);
+ int (*ipset_entry_notify_owner)(int command,
+ struct zclient *zclient,
+ uint16_t length,
+ vrf_id_t vrf_id);
};
/* Zebra API message flag. */
ZAPI_RULE_REMOVED,
};
+enum ipset_type {
+ IPSET_NET_NET = 1,
+ IPSET_NET_PORT_NET,
+ IPSET_NET_PORT,
+ IPSET_NET
+};
+
+enum zapi_ipset_notify_owner {
+ ZAPI_IPSET_FAIL_INSTALL,
+ ZAPI_IPSET_INSTALLED,
+ ZAPI_IPSET_REMOVED,
+};
+
+enum zapi_ipset_entry_notify_owner {
+ ZAPI_IPSET_ENTRY_FAIL_INSTALL,
+ ZAPI_IPSET_ENTRY_INSTALLED,
+ ZAPI_IPSET_ENTRY_REMOVED,
+};
+
+enum zapi_iptable_notify_owner {
+ ZAPI_IPTABLE_FAIL_INSTALL,
+ ZAPI_IPTABLE_INSTALLED,
+ ZAPI_IPTABLE_REMOVED,
+};
+
/* Zebra MAC types */
#define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/
#define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/
extern struct interface *zebra_interface_link_params_read(struct stream *);
extern size_t zebra_interface_link_params_write(struct stream *,
struct interface *);
+extern int zclient_send_get_label_chunk(
+ struct zclient *zclient,
+ uint8_t keep,
+ uint32_t chunk_size);
+
extern int lm_label_manager_connect(struct zclient *zclient);
extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep,
uint32_t chunk_size, uint32_t *start,
uint32_t *priority, uint32_t *unique,
ifindex_t *ifindex,
enum zapi_rule_notify_owner *note);
+bool zapi_ipset_notify_decode(struct stream *s,
+ uint32_t *unique,
+ enum zapi_ipset_notify_owner *note);
+
+#define ZEBRA_IPSET_NAME_SIZE 32
+
+bool zapi_ipset_entry_notify_decode(struct stream *s,
+ uint32_t *unique,
+ char *ipset_name,
+ enum zapi_ipset_entry_notify_owner *note);
+
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
extern bool zapi_nexthop_update_decode(struct stream *s,
struct zapi_route *nhr);
/* (3) if the prefix is equal to an active configured address range */
/* or if the NU bit is set in the prefix */
if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) {
+ /* must have been set in previous block */
+ assert(prefix_lsa);
+
range = ospf6_route_lookup(&prefix, oa->range_table);
if (range) {
if (is_debug)
ospf6_interface_state_update(oi->interface);
}
-void ospf6_interface_if_del(struct interface *ifp)
-{
- struct ospf6_interface *oi;
-
- oi = (struct ospf6_interface *)ifp->info;
- if (oi == NULL)
- return;
-
- /* interface stop */
- if (oi->area)
- thread_execute(master, interface_down, oi, 0);
-
- listnode_delete(oi->area->if_list, oi);
- oi->area = (struct ospf6_area *)NULL;
-
- /* cut link */
- oi->interface = NULL;
- ifp->info = NULL;
-
- ospf6_interface_delete(oi);
-}
-
void ospf6_interface_state_update(struct interface *ifp)
{
struct ospf6_interface *oi;
extern void ospf6_interface_disable(struct ospf6_interface *);
extern void ospf6_interface_if_add(struct interface *);
-extern void ospf6_interface_if_del(struct interface *);
extern void ospf6_interface_state_update(struct interface *);
extern void ospf6_interface_connected_route_update(struct interface *);
INTRA_PREFIX)) {
prefix2str(&route->prefix, buf,
sizeof(buf));
+ assert(route->nh_list);
zlog_debug("%s: route %s update paths %u nh %u"
, __PRETTY_FUNCTION__,
buf,
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(on->ospf6_if->area);
- if (prev_state == OSPF6_NEIGHBOR_LOADING
- && next_state == OSPF6_NEIGHBOR_FULL) {
+ if ((prev_state == OSPF6_NEIGHBOR_LOADING ||
+ prev_state == OSPF6_NEIGHBOR_EXCHANGE) &&
+ next_state == OSPF6_NEIGHBOR_FULL) {
OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if);
on->ospf6_if->area->full_nbrs++;
}
zlog_debug("Zebra Interface delete: %s index %d mtu %d",
ifp->name, ifp->ifindex, ifp->mtu6);
-#if 0
- /* XXX: ospf6_interface_if_del is not the right way to handle this,
- * because among other thinkable issues, it will also clear all
- * settings as they are contained in the struct ospf6_interface. */
- ospf6_interface_if_del (ifp);
-#endif /*0*/
-
if_set_index(ifp, IFINDEX_INTERNAL);
return 0;
}
ei.route_map_set.metric = -1;
ei.route_map_set.metric_type = -1;
ei.tag = 0;
+ ei.instance = 0;
if ((new = ospf_external_lsa_new(ospf, &ei, &type7->data->id))
== NULL) {
struct ospf_neighbor *nbr;
struct route_node *rn;
struct ospf_neighbor *min = NULL;
- struct ospf *ospf = ospf;
+ struct ospf *ospf;
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
/* Set opaque-LSA header fields depending of the type of RFC */
if (IS_INTER_AS(lp->type)) {
- if
- IS_FLOOD_AS(lp->type)
- {
- options |= OSPF_OPTION_E; /* Enable AS external
- as we flood
- Inter-AS with
- Opaque Type 11 */
- lsa_type = OSPF_OPAQUE_AS_LSA;
- }
- else {
+ if (IS_FLOOD_AS(lp->type)) {
+ /* Enable AS external as we flood Inter-AS with Opaque
+ * Type 11
+ */
+ options |= OSPF_OPTION_E;
+ lsa_type = OSPF_OPAQUE_AS_LSA;
+ } else {
options |= LSA_OPTIONS_GET(
area); /* Get area default option */
options |= LSA_OPTIONS_NSSA_GET(area);
/* Now, create an OSPF LSA instance. */
if ((new = ospf_lsa_new()) == NULL) {
- zlog_warn("ospf_mpls_te_lsa_new: ospf_lsa_new() ?");
+ zlog_warn("%s: ospf_lsa_new() ?", __func__);
stream_free(s);
return NULL;
}
if ((new->data = ospf_lsa_data_new(length)) == NULL) {
- zlog_warn("ospf_mpls_te_lsa_new: ospf_lsa_data_new() ?");
+ zlog_warn("%s: ospf_lsa_data_new() ?", __func__);
ospf_lsa_unlock(&new);
new = NULL;
stream_free(s);
assert(ifp->info);
- if (!OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) {
+ if (IF_DEF_PARAMS(ifp)
+ && !OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) {
SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp);
}
"debug pbr events",
};
-/*
- * Set or unset flags on all debugs for pbrd.
- *
- * flags
- * The flags to set
- *
- * set
- * Whether to set or unset the specified flags
- */
-static void pbr_debug_set_all(uint32_t flags, bool set)
+void pbr_debug_set_all(uint32_t flags, bool set)
{
for (unsigned int i = 0; i < array_size(pbr_debugs); i++) {
DEBUG_FLAGS_SET(pbr_debugs[i], flags, set);
}
}
-/*
- * Check flags on all debugs for pbrd.
- *
- * flags
- * The flags to set
- *
- * Returns:
- * The subset of the given flags that were set in all pbrd debugs
- */
-static uint32_t pbr_debug_check_all(uint32_t flags)
-{
- uint32_t mode = DEBUG_MODE_ALL;
-
- for (unsigned int i = 0; i < array_size(pbr_debugs); i++)
- mode &= DEBUG_MODE_CHECK(pbr_debugs[i], flags);
- return mode;
-}
-
-static int pbr_debug_config_write_helper(struct vty *vty, bool config)
+int pbr_debug_config_write_helper(struct vty *vty, bool config)
{
uint32_t mode = DEBUG_MODE_ALL;
if (config)
mode = DEBUG_MODE_CONF;
- if (pbr_debug_check_all(DEBUG_MODE_CONF) == mode) {
- vty_out(vty, "debug pbr\n");
- return 0;
- }
-
for (unsigned int i = 0; i < array_size(pbr_debugs); i++)
if (DEBUG_MODE_CHECK(pbr_debugs[i], mode))
vty_out(vty, "%s\n", pbr_debugs_conflines[i]);
return pbr_debug_config_write_helper(vty, true);
}
-/* PBR debugging CLI ------------------------------------------------------- */
-/* clang-format off */
-
-DEFPY(debug_pbr,
- debug_pbr_cmd,
- "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
- NO_STR
- DEBUG_STR
- "Policy Based Routing\n"
- "Policy maps\n"
- "PBRD <-> Zebra communications\n"
- "Nexthop tracking\n"
- "Events\n")
-{
- uint32_t mode = DEBUG_NODE2MODE(vty->node);
-
- if (map)
- DEBUG_MODE_SET(&pbr_dbg_map, mode, !no);
- if (zebra)
- DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no);
- if (nht)
- DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no);
- if (events)
- DEBUG_MODE_SET(&pbr_dbg_event, mode, !no);
-
- /* no specific debug --> act on all of them */
- if (strmatch(argv[argc - 1]->text, "pbr"))
- pbr_debug_set_all(mode, !no);
-
- return CMD_SUCCESS;
-}
-
-DEFUN_NOSH(show_debugging_pbr,
- show_debugging_pbr_cmd,
- "show debugging [pbr]",
- SHOW_STR
- DEBUG_STR
- "Policy Based Routing\n")
-{
- vty_out(vty, "PBR debugging status:\n");
-
- pbr_debug_config_write_helper(vty, false);
-
- return CMD_SUCCESS;
-}
-
-/* clang-format on */
-/* ------------------------------------------------------------------------- */
-
-static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
-
struct debug_callbacks pbr_dbg_cbs = {.debug_set_all = pbr_debug_set_all};
void pbr_debug_init(void)
{
debug_init(&pbr_dbg_cbs);
}
-
-void pbr_debug_init_vty(void)
-{
- install_node(&debug_node, pbr_debug_config_write);
-
- install_element(VIEW_NODE, &debug_pbr_cmd);
- install_element(CONFIG_NODE, &debug_pbr_cmd);
-
- install_element(VIEW_NODE, &show_debugging_pbr_cmd);
-}
void pbr_debug_init(void);
/*
- * Install PBR debugging VTY commands.
+ * Set or unset flags on all debugs for pbrd.
+ *
+ * flags
+ * The flags to set
+ *
+ * set
+ * Whether to set or unset the specified flags
+ */
+void pbr_debug_set_all(uint32_t flags, bool set);
+
+/*
+ * Config write helper.
+ *
+ * vty
+ * Vty to write to
+ *
+ * config
+ * Whether we are writing to show run or saving config file
+ *
+ * Returns:
+ * 0 for convenience
*/
-void pbr_debug_init_vty(void);
+int pbr_debug_config_write_helper(struct vty *vty, bool config);
/*
* Print PBR debugging configuration.
pbr_nhgroup_del_nexthop_cb,
pbr_nhgroup_delete_cb);
+ /*
+ * So we safely ignore these commands since
+ * we are getting them at this point in time
+ */
+ access_list_init();
pbr_nht_init();
pbr_map_init();
pbr_zebra_init();
pmi->pbrm = pbrm;
listnode_add_sort(pbrm->incoming, pmi);
+ bf_assign_index(pbrm->ifi_bitfield, pmi->install_bit);
pbr_map_check_valid(pbrm->name);
- if (pbrm->valid && !pbrm->installed)
+ if (pbrm->valid)
pbr_map_install(pbrm);
}
if (pbrm->seqnumbers->count == 0) {
RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm);
+
+ bf_free(pbrm->ifi_bitfield);
XFREE(MTYPE_PBR_MAP, pbrm);
}
}
pbrm->valid = false;
pbrms->nhs_installed = false;
- pbrms->installed = false;
pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
pbrms->nhgrp_name = NULL;
}
-struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique,
- ifindex_t ifindex)
+struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex,
+ struct pbr_map_interface **ppmi)
{
struct pbr_map_sequence *pbrms;
struct listnode *snode, *inode;
if (pmi->ifp->ifindex != ifindex)
continue;
+ if (ppmi)
+ *ppmi = pmi;
+
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode,
pbrms)) {
DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u",
pbrm = pbrm_find(name);
if (!pbrm) {
pbrm = XCALLOC(MTYPE_PBR_MAP, sizeof(*pbrm));
- strcpy(pbrm->name, name);
+ snprintf(pbrm->name, sizeof(pbrm->name), "%s", name);
pbrm->seqnumbers = list_new();
pbrm->seqnumbers->cmp =
RB_INSERT(pbr_map_entry_head, &pbr_maps, pbrm);
+ bf_init(pbrm->ifi_bitfield, 64);
pbr_map_add_interfaces(pbrm);
}
QOBJ_REG(pbrms, pbr_map_sequence);
listnode_add_sort(pbrm->seqnumbers, pbrms);
-
- pbrm->installed = false;
}
return pbrms;
listnode_delete(pbrm->incoming, pmi);
pmi->pbrm = NULL;
+
+ bf_release_index(pbrm->ifi_bitfield, pmi->install_bit);
XFREE(MTYPE_PBR_MAP_INTERFACE, pmi);
}
pbrms->seqno, pbrms->reason);
}
- for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) {
pbr_send_pbr_map(pbrms, pmi, install);
+ }
}
void pbr_map_install(struct pbr_map *pbrm)
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
pbr_send_pbr_map(pbrms, pmi, true);
-
- pbrm->installed = true;
}
void pbr_map_init(void)
#ifndef __PBR_MAP_H__
#define __PBR_MAP_H__
+#include <bitfield.h>
+
struct pbr_map {
/*
* RB Tree of the pbr_maps
*/
struct list *incoming;
+ bitfield_t ifi_bitfield;
/*
* If valid is true we think the pbr_map is valid,
* If false, look in individual pbrms to see
* what we think is the invalid reason
*/
bool valid;
-
- bool installed;
};
RB_HEAD(pbr_map_entry_head, pbr_map);
RB_PROTOTYPE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare)
struct pbr_map_interface {
+ uint32_t install_bit;
+
struct interface *ifp;
struct pbr_map *pbrm;
/*
* Are we installed
*/
- bool installed;
+ uint64_t installed;
/*
* A reason of 0 means we think the pbr_map_sequence is good to go
extern struct pbr_map_entry_head pbr_maps;
extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno);
-extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique,
- ifindex_t ifindex);
+extern struct pbr_map_sequence *
+pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex,
+ struct pbr_map_interface **ppmi);
extern struct pbr_map *pbrm_find(const char *name);
extern void pbr_map_delete(struct pbr_map_sequence *pbrms);
struct nexthop_group_cmd *nhgc;
nhgc = nhgc_find(name);
+
+ if (!nhgc) {
+ DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n",
+ __PRETTY_FUNCTION__, name);
+ return;
+ }
+
pnhgc = pbr_nht_add_group(name);
DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__,
if (pnhgc->table_id == *table_id) {
DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s",
__PRETTY_FUNCTION__, *table_id, pnhgc->name);
- pnhgc->installed = true;
- pbr_map_schedule_policy_from_nhg(pnhgc->name);
+
+ /*
+ * If the table has been re-handled by zebra
+ * and we are already installed no need to do
+ * anything here.
+ */
+ if (!pnhgc->installed) {
+ pnhgc->installed = true;
+ pbr_map_schedule_policy_from_nhg(pnhgc->name);
+ }
}
}
install_afi = pbr_nht_which_afi(nhg, nh_afi);
- pnhgc->installed = false;
-
route_add(pnhgc, nhg, install_afi);
}
return;
memset(&find, 0, sizeof(find));
- strcpy(find.name, name);
+ snprintf(find.name, sizeof(find.name), "%s", name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
if (!pnhgc) {
pbrm->valid = false;
pbrms->nhs_installed = false;
- pbrms->installed = false;
pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
memset(&find, 0, sizeof(find));
- strcpy(&find.name[0], pbrms->internal_nhg_name);
+ snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
nh = pbrms->nhg->nexthop;
return NULL;
}
- strcpy(lookup.name, name);
+ snprintf(lookup.name, sizeof(lookup.name), "%s", name);
pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__,
pnhgc);
DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name);
- strcpy(lookup.name, name);
+ snprintf(lookup.name, sizeof(lookup.name), "%s", name);
pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
if (!pnhgc)
return false;
struct pbr_nexthop_group_cache *pnhgc;
memset(&find, 0, sizeof(find));
- strcpy(find.name, name);
+ snprintf(find.name, sizeof(find.name), "%s", name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
if (!pnhgc) {
struct pbr_nexthop_group_cache *pnhgc;
memset(&find, 0, sizeof(find));
- strcpy(find.name, name);
+ snprintf(find.name, sizeof(find.name), "%s", name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
intf, vrf->name);
return CMD_WARNING_CONFIG_FAILED;
}
- } else
+ } else {
+ if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
+ vty_out(vty,
+ "Specified a v6 LL with no interface, rejecting\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
nhop.type = NEXTHOP_TYPE_IPV6;
+ }
}
if (pbrms->nhg)
pbrm = pbrm_find(mapname);
if (!pbr_ifp) {
- /*
- * Some one could have fat fingered the interface
- * name
- */
+ /* we don't want one and we don't have one, so... */
+ if (no)
+ return CMD_SUCCESS;
+
+ /* Some one could have fat fingered the interface name */
pbr_ifp = pbr_if_new(ifp);
}
if (no) {
if (strcmp(pbr_ifp->mapname, mapname) == 0) {
- strcpy(pbr_ifp->mapname, "");
-
+ pbr_ifp->mapname[0] = '\0';
if (pbrm)
pbr_map_interface_delete(pbrm, ifp);
}
} else {
- if (strcmp(pbr_ifp->mapname, "") == 0) {
- strcpy(pbr_ifp->mapname, mapname);
-
- if (pbrm)
- pbr_map_add_interface(pbrm, ifp);
- } else {
- if (!(strcmp(pbr_ifp->mapname, mapname) == 0)) {
- old_pbrm = pbrm_find(pbr_ifp->mapname);
- if (old_pbrm)
- pbr_map_interface_delete(old_pbrm, ifp);
- strcpy(pbr_ifp->mapname, mapname);
- if (pbrm)
- pbr_map_add_interface(pbrm, ifp);
- }
+ if (strcmp(pbr_ifp->mapname, "") != 0) {
+ old_pbrm = pbrm_find(pbr_ifp->mapname);
+ if (old_pbrm)
+ pbr_map_interface_delete(old_pbrm, ifp);
}
+ snprintf(pbr_ifp->mapname, sizeof(pbr_ifp->mapname),
+ "%s", mapname);
+ if (pbrm)
+ pbr_map_add_interface(pbrm, ifp);
}
return CMD_SUCCESS;
pbr_map_reason_string(pbrms->reason, rbuf,
sizeof(rbuf));
vty_out(vty,
- " Seq: %u rule: %u Installed: %d(%u) Reason: %s\n",
+ " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n",
pbrms->seqno, pbrms->ruleno, pbrms->installed,
pbrms->unique, pbrms->reason ? rbuf : "Valid");
return CMD_SUCCESS;
}
+/* PBR debugging CLI ------------------------------------------------------- */
+/* clang-format off */
+
+static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
+
+DEFPY(debug_pbr,
+ debug_pbr_cmd,
+ "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
+ NO_STR
+ DEBUG_STR
+ "Policy Based Routing\n"
+ "Policy maps\n"
+ "PBRD <-> Zebra communications\n"
+ "Nexthop tracking\n"
+ "Events\n")
+{
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+ if (map)
+ DEBUG_MODE_SET(&pbr_dbg_map, mode, !no);
+ if (zebra)
+ DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no);
+ if (nht)
+ DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no);
+ if (events)
+ DEBUG_MODE_SET(&pbr_dbg_event, mode, !no);
+
+ /* no specific debug --> act on all of them */
+ if (strmatch(argv[argc - 1]->text, "pbr"))
+ pbr_debug_set_all(mode, !no);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH(show_debugging_pbr,
+ show_debugging_pbr_cmd,
+ "show debugging [pbr]",
+ SHOW_STR
+ DEBUG_STR
+ "Policy Based Routing\n")
+{
+ vty_out(vty, "PBR debugging status:\n");
+
+ pbr_debug_config_write_helper(vty, false);
+
+ return CMD_SUCCESS;
+}
+
+/* clang-format on */
+/* ------------------------------------------------------------------------- */
+
+
static struct cmd_node interface_node = {
INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
};
install_node(&pbr_map_node,
pbr_vty_map_config_write);
+ /* debug */
+ install_node(&debug_node, pbr_debug_config_write);
+ install_element(VIEW_NODE, &debug_pbr_cmd);
+ install_element(CONFIG_NODE, &debug_pbr_cmd);
+ install_element(VIEW_NODE, &show_debugging_pbr_cmd);
+
install_default(PBRMAP_NODE);
install_element(CONFIG_NODE, &pbr_map_cmd);
install_element(VIEW_NODE, &show_pbr_map_cmd);
install_element(VIEW_NODE, &show_pbr_interface_cmd);
install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd);
-
- pbr_debug_init_vty();
}
/* Zebra structure to hold current status. */
struct zclient *zclient;
-static struct interface *zebra_interface_if_lookup(struct stream *s)
-{
- char ifname_tmp[INTERFACE_NAMSIZ];
-
- /* Read interface name. */
- stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);
-
- /* And look it up. */
- return if_lookup_by_name(ifname_tmp, VRF_DEFAULT);
-}
-
struct pbr_interface *pbr_if_new(struct interface *ifp)
{
struct pbr_interface *pbr_ifp;
zebra_size_t length, vrf_id_t vrf_id)
{
- zebra_interface_if_lookup(zclient->ibuf);
+ zebra_interface_state_read(zclient->ibuf, vrf_id);
return 0;
}
uint32_t seqno, priority, unique;
enum zapi_rule_notify_owner note;
struct pbr_map_sequence *pbrms;
+ struct pbr_map_interface *pmi;
ifindex_t ifi;
+ uint64_t installed;
if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
&ifi, ¬e))
return -1;
- pbrms = pbrms_lookup_unique(unique, ifi);
+ pmi = NULL;
+ pbrms = pbrms_lookup_unique(unique, ifi, &pmi);
if (!pbrms) {
DEBUGD(&pbr_dbg_zebra,
"%s: Failure to lookup pbrms based upon %u",
return 0;
}
+ installed = 1 << pmi->install_bit;
+
switch (note) {
case ZAPI_RULE_FAIL_INSTALL:
DEBUGD(&pbr_dbg_zebra, "%s: Recieved RULE_FAIL_INSTALL",
__PRETTY_FUNCTION__);
- pbrms->installed = false;
+ pbrms->installed &= ~installed;
break;
case ZAPI_RULE_INSTALLED:
- pbrms->installed = true;
+ pbrms->installed |= installed;
DEBUGD(&pbr_dbg_zebra, "%s: Recived RULE_INSTALLED",
__PRETTY_FUNCTION__);
break;
case ZAPI_RULE_REMOVED:
+ pbrms->installed &= ~installed;
DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED",
__PRETTY_FUNCTION__);
break;
{
struct pbr_map *pbrm = pbrms->parent;
struct stream *s;
+ uint64_t is_installed = 1 << pmi->install_bit;
- DEBUGD(&pbr_dbg_zebra, "%s: for %s %d", __PRETTY_FUNCTION__, pbrm->name,
- install);
+ is_installed &= pbrms->installed;
+
+ DEBUGD(&pbr_dbg_zebra, "%s: for %s %d(%" PRIu64 ")",
+ __PRETTY_FUNCTION__, pbrm->name, install, is_installed);
+
+ /*
+ * If we are installed and asked to do so again
+ * just return. If we are not installed and asked
+ * and asked to delete just return;
+ */
+ if (install && is_installed)
+ return;
+
+ if (!install && !is_installed)
+ return;
s = zclient->obuf;
stream_reset(s);
ret = rtnl_open(&rth, 0);
- if (ret < 0)
+ if (ret < 0 || rth.fd <= 0)
return ret;
if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
static void lsa_check_resize(size_t len)
{
+ struct ospf6_lsa **templsas;
+
if (lsa_count >= len)
return;
- lsas = realloc(lsas, len * sizeof(lsas[0]));
+ templsas = realloc(lsas, len * sizeof(lsas[0]));
+ if (templsas)
+ lsas = templsas;
+ else
+ return;
memset(lsas + lsa_count, 0, sizeof(lsas[0]) * (len - lsa_count));
lsa_count = len;
MAX_INSTANCES=5
RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py
-. /lib/lsb/init-functions
+if [ -e /lib/lsb/init-functions ]; then
+ . /lib/lsb/init-functions
+fi
if [ -f /usr/lib/frr/ssd ]; then
SSD=/usr/lib/frr/ssd
leak:clippy
+leak:PyObject_Malloc
+leak:PyObject_Realloc
+leak:PyList_Append
+leak:malloc
char *homedir = NULL;
int ditch_suid = 0;
char sysconfdir[MAXPATHLEN];
+ const char *pathspace_arg = NULL;
char pathspace[MAXPATHLEN] = "";
/* SUID: drop down to calling user & go back up when needed */
"slashes or dots are not permitted in the --pathspace option.\n");
exit(1);
}
- snprintf(pathspace, sizeof(pathspace), "/%s", optarg);
+ pathspace_arg = optarg;
+ snprintf(pathspace, sizeof(pathspace), "%s/", optarg);
break;
case 'd':
daemon_name = optarg;
pathspace, VTYSH_CONFIG_NAME);
snprintf(frr_config, sizeof(frr_config), "%s%s%s", sysconfdir,
pathspace, FRR_CONFIG_NAME);
- strlcat(vtydir, pathspace, sizeof(vtydir));
+
+ if (pathspace_arg) {
+ strlcat(vtydir, "/", sizeof(vtydir));
+ strlcat(vtydir, pathspace_arg, sizeof(vtydir));
+ }
/* Initialize user input buffer. */
line_read = NULL;
continue;
}
- for (i = 0; i < 10; i++) {
- if (!zebra_type[i].str)
- break;
+ i = 0;
+ while (zebra_type[i++].str) {
if (strcmp(zebra_type[i].str, str) == 0) {
type = zebra_type[i].type;
break;
/* Add connected IPv6 route to the interface. */
void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
- uint8_t prefixlen, const char *label)
+ struct in6_addr *broad, uint8_t prefixlen,
+ const char *label)
{
struct prefix_ipv6 *p;
struct connected *ifc;
p->prefixlen = prefixlen;
ifc->address = (struct prefix *)p;
+ if (broad) {
+ p = prefix_ipv6_new();
+ p->family = AF_INET6;
+ IPV6_ADDR_COPY(&p->prefix, broad);
+ p->prefixlen = prefixlen;
+ ifc->destination = (struct prefix *)p;
+ } else {
+ if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) {
+ zlog_warn("warning: %s called for interface %s with peer flag set, but no peer address supplied",
+ __func__, ifp->name);
+ UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+ }
+ }
+
/* Label of this address. */
if (label)
ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
}
void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
- uint8_t prefixlen)
+ struct in6_addr *broad, uint8_t prefixlen)
{
- struct prefix p;
+ struct prefix p, d;
struct connected *ifc;
memset(&p, 0, sizeof(struct prefix));
memcpy(&p.u.prefix6, address, sizeof(struct in6_addr));
p.prefixlen = prefixlen;
- ifc = connected_check(ifp, &p);
+ if (broad) {
+ memset(&d, 0, sizeof(struct prefix));
+ d.family = AF_INET6;
+ IPV6_ADDR_COPY(&d.u.prefix, broad);
+ d.prefixlen = prefixlen;
+ ifc = connected_check_ptp(ifp, &p, &d);
+ } else
+ ifc = connected_check_ptp(ifp, &p, NULL);
connected_delete_helper(ifc, &p);
}
extern void connected_down(struct interface *ifp, struct connected *ifc);
extern void connected_add_ipv6(struct interface *ifp, int flags,
- struct in6_addr *address, uint8_t prefixlen,
- const char *label);
+ struct in6_addr *address, struct in6_addr *broad,
+ uint8_t prefixlen, const char *label);
extern void connected_delete_ipv6(struct interface *ifp,
- struct in6_addr *address, uint8_t prefixlen);
+ struct in6_addr *address,
+ struct in6_addr *broad, uint8_t prefixlen);
extern int connected_is_unnumbered(struct interface *);
}
#endif
- connected_add_ipv6(ifp, flags, &addr->sin6_addr,
+ connected_add_ipv6(ifp, flags, &addr->sin6_addr, NULL,
prefixlen, NULL);
}
}
connected_add_ipv4(ifp, flags, &SIN(addr)->sin_addr, prefixlen,
(struct in_addr *)dest_pnt, label);
else if (af == AF_INET6)
- connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr,
+ connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr, NULL,
prefixlen, label);
return 0;
& (IFA_F_DADFAILED | IFA_F_TENTATIVE)))
connected_add_ipv6(ifp, flags,
(struct in6_addr *)addr,
+ (struct in6_addr *)broad,
ifa->ifa_prefixlen, label);
} else
connected_delete_ipv6(ifp, (struct in6_addr *)addr,
+ (struct in6_addr *)broad,
ifa->ifa_prefixlen);
}
if (ifam->ifam_type == RTM_NEWADDR)
connected_add_ipv6(ifp, flags, &addr.sin6.sin6_addr,
+ NULL,
ip6_masklen(mask.sin6.sin6_addr),
(isalias ? ifname : NULL));
else
- connected_delete_ipv6(ifp, &addr.sin6.sin6_addr,
+ connected_delete_ipv6(ifp, &addr.sin6.sin6_addr, NULL,
ip6_masklen(mask.sin6.sin6_addr));
break;
default:
re->tag, rmap_name);
if (ret != RMAP_MATCH) {
+ UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED);
zebra_del_import_table_entry(rn, re);
return 0;
}
break;
}
- if (same)
+ if (same) {
+ UNSET_FLAG(same->flags, ZEBRA_FLAG_SELECTED);
zebra_del_import_table_entry(rn, same);
+ }
newre = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
newre->type = ZEBRA_ROUTE_TABLE;
char buf2[PREFIX_STRLEN];
memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
- family = PREFIX_FAMILY(&rule->filter.src_ip);
+ family = PREFIX_FAMILY(&rule->rule.filter.src_ip);
bytelen = (family == AF_INET ? 4 : 16);
req.n.nlmsg_type = cmd;
req.frh.action = FR_ACT_TO_TBL;
/* rule's pref # */
- addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->priority);
+ addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority);
/* interface on which applied */
if (rule->ifp)
/* source IP, if specified */
if (IS_RULE_FILTERING_ON_SRC_IP(rule)) {
- req.frh.src_len = rule->filter.src_ip.prefixlen;
+ req.frh.src_len = rule->rule.filter.src_ip.prefixlen;
addattr_l(&req.n, sizeof(req), FRA_SRC,
- &rule->filter.src_ip.u.prefix, bytelen);
+ &rule->rule.filter.src_ip.u.prefix, bytelen);
}
/* destination IP, if specified */
if (IS_RULE_FILTERING_ON_DST_IP(rule)) {
- req.frh.dst_len = rule->filter.dst_ip.prefixlen;
+ req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen;
addattr_l(&req.n, sizeof(req), FRA_DST,
- &rule->filter.dst_ip.u.prefix, bytelen);
+ &rule->rule.filter.dst_ip.u.prefix, bytelen);
}
/* Route table to use to forward, if filter criteria matches. */
- if (rule->action.table < 256)
- req.frh.table = rule->action.table;
+ if (rule->rule.action.table < 256)
+ req.frh.table = rule->rule.action.table;
else {
req.frh.table = RT_TABLE_UNSPEC;
addattr32(&req.n, sizeof(req), FRA_TABLE,
- rule->action.table);
+ rule->rule.action.table);
}
if (IS_ZEBRA_DEBUG_KERNEL)
"Tx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
nl_msg_type_to_str(cmd), nl_family_to_str(family),
rule->ifp ? rule->ifp->name : "Unknown",
- rule->ifp ? rule->ifp->ifindex : 0, rule->priority,
- prefix2str(&rule->filter.src_ip, buf1, sizeof(buf1)),
- prefix2str(&rule->filter.dst_ip, buf2, sizeof(buf2)),
- rule->action.table);
+ rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority,
+ prefix2str(&rule->rule.filter.src_ip, buf1,
+ sizeof(buf1)),
+ prefix2str(&rule->rule.filter.dst_ip, buf2,
+ sizeof(buf2)),
+ rule->rule.action.table);
/* Ship off the message.
* Note: Currently, netlink_talk() is a blocking call which returns
memset(&rule, 0, sizeof(rule));
if (tb[FRA_PRIORITY])
- rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]);
+ rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]);
if (tb[FRA_SRC]) {
if (frh->family == AF_INET)
- memcpy(&rule.filter.src_ip.u.prefix4,
+ memcpy(&rule.rule.filter.src_ip.u.prefix4,
RTA_DATA(tb[FRA_SRC]), 4);
else
- memcpy(&rule.filter.src_ip.u.prefix6,
+ memcpy(&rule.rule.filter.src_ip.u.prefix6,
RTA_DATA(tb[FRA_SRC]), 16);
- rule.filter.src_ip.prefixlen = frh->src_len;
- rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
+ rule.rule.filter.src_ip.prefixlen = frh->src_len;
+ rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
}
if (tb[FRA_DST]) {
if (frh->family == AF_INET)
- memcpy(&rule.filter.dst_ip.u.prefix4,
+ memcpy(&rule.rule.filter.dst_ip.u.prefix4,
RTA_DATA(tb[FRA_DST]), 4);
else
- memcpy(&rule.filter.dst_ip.u.prefix6,
+ memcpy(&rule.rule.filter.dst_ip.u.prefix6,
RTA_DATA(tb[FRA_DST]), 16);
- rule.filter.dst_ip.prefixlen = frh->dst_len;
- rule.filter.filter_bm |= PBR_FILTER_DST_IP;
+ rule.rule.filter.dst_ip.prefixlen = frh->dst_len;
+ rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
}
if (tb[FRA_TABLE])
- rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]);
+ rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]);
else
- rule.action.table = frh->table;
+ rule.rule.action.table = frh->table;
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
nl_msg_type_to_str(h->nlmsg_type),
nl_family_to_str(frh->family), rule.ifp->name,
- rule.ifp->ifindex, rule.priority,
- prefix2str(&rule.filter.src_ip, buf1, sizeof(buf1)),
- prefix2str(&rule.filter.dst_ip, buf2, sizeof(buf2)),
- rule.action.table);
+ rule.ifp->ifindex, rule.rule.priority,
+ prefix2str(&rule.rule.filter.src_ip, buf1,
+ sizeof(buf1)),
+ prefix2str(&rule.rule.filter.dst_ip, buf2,
+ sizeof(buf2)),
+ rule.rule.action.table);
return kernel_pbr_rule_del(&rule);
}
}
}
-/*
- * String to label conversion, labels separated by '/'.
- *
- * @param label_str labels separated by /
- * @param num_labels number of labels; zero if conversion was unsuccessful
- * @param labels preallocated mpls_label_t array of size MPLS_MAX_LABELS; only
- * modified if the conversion succeeded
- * @return 0 on success
- * -1 if the string could not be parsed as integers
- * -2 if a label was inside the reserved range (0-15)
- * -3 if the number of labels given exceeds MPLS_MAX_LABELS
- */
-int mpls_str2label(const char *label_str, uint8_t *num_labels,
- mpls_label_t *labels)
-{
- char *ostr; // copy of label string (start)
- char *lstr; // copy of label string
- char *nump; // pointer to next segment
- char *endp; // end pointer
- int i; // for iterating label_str
- int rc; // return code
- mpls_label_t pl[MPLS_MAX_LABELS]; // parsed labels
-
- /* labels to zero until we have a successful parse */
- ostr = lstr = XSTRDUP(MTYPE_TMP, label_str);
- *num_labels = 0;
- rc = 0;
-
- for (i = 0; i < MPLS_MAX_LABELS && lstr && !rc; i++) {
- nump = strsep(&lstr, "/");
- pl[i] = strtoul(nump, &endp, 10);
-
- /* format check */
- if (*endp != '\0')
- rc = -1;
- /* validity check */
- else if (!IS_MPLS_UNRESERVED_LABEL(pl[i]))
- rc = -2;
- }
-
- /* excess labels */
- if (!rc && i == MPLS_MAX_LABELS && lstr)
- rc = -3;
-
- if (!rc) {
- *num_labels = i;
- memcpy(labels, pl, *num_labels * sizeof(mpls_label_t));
- }
-
- XFREE(MTYPE_TMP, ostr);
-
- return rc;
-}
-
-/*
- * Label to string conversion, labels in string separated by '/'.
- */
-char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf,
- int len, int pretty)
-{
- char label_buf[BUFSIZ];
- int i;
-
- buf[0] = '\0';
- for (i = 0; i < num_labels; i++) {
- if (i != 0)
- strlcat(buf, "/", len);
- if (pretty)
- label2str(labels[i], label_buf, sizeof(label_buf));
- else
- snprintf(label_buf, sizeof(label_buf), "%u", labels[i]);
- strlcat(buf, label_buf, len);
- }
-
- return buf;
-}
-
/*
* Install dynamic LSP entry.
*/
? AF_INET6 \
: AF_INET)
-#define MPLS_LABEL_HELPSTR \
- "Specify label(s) for this route\nOne or more " \
- "labels in the range (16-1048575) separated by '/'\n"
-
/* Typedefs */
typedef struct zebra_ile_t_ zebra_ile_t;
/* Function declarations. */
-/*
- * String to label conversion, labels separated by '/'.
- */
-int mpls_str2label(const char *label_str, uint8_t *num_labels,
- mpls_label_t *labels);
-
-/*
- * Label to string conversion, labels in string separated by '/'.
- */
-char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf,
- int len, int pretty);
-
/*
* Add/update global label block.
*/
char *netnspath = ns_netns_pathname(NULL, name);
struct vrf *vrf;
int ret;
- ns_id_t ns_id;
+ ns_id_t ns_id, ns_id_external;
if (netnspath == NULL)
return;
ns_id = zebra_ns_id_get(netnspath);
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
+ ns_id_external = ns_map_nsid_with_external(ns_id, true);
/* if VRF with NS ID already present */
- vrf = vrf_lookup_by_id((vrf_id_t)ns_id);
+ vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external);
if (vrf) {
zlog_warn(
"NS notify : same NSID used by VRF %s. Ignore NS %s creation",
}
if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) {
zlog_warn("NS notify : failed to create VRF %s", name);
+ ns_map_nsid_with_external(ns_id, false);
return;
}
if (zserv_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
- ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id);
+ ret = vrf_netns_handler_create(NULL, vrf, netnspath,
+ ns_id_external, ns_id);
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
if (ret != CMD_SUCCESS) {
zlog_warn("NS notify : failed to create NS %s", netnspath);
+ ns_map_nsid_with_external(ns_id, false);
return;
}
zlog_info("NS notify : created VRF %s NS %s", name, netnspath);
hash_create_size(8, zebra_pbr_rules_hash_key,
zebra_pbr_rules_hash_equal, "Rules Hash");
+ zns->ipset_hash =
+ hash_create_size(8, zebra_pbr_ipset_hash_key,
+ zebra_pbr_ipset_hash_equal, "IPset Hash");
+
+ zns->ipset_entry_hash =
+ hash_create_size(8, zebra_pbr_ipset_entry_hash_key,
+ zebra_pbr_ipset_entry_hash_equal,
+ "IPset Hash Entry");
+
+ zns->iptable_hash =
+ hash_create_size(8, zebra_pbr_iptable_hash_key,
+ zebra_pbr_iptable_hash_equal,
+ "IPtable Hash Entry");
+
#if defined(HAVE_RTADV)
rtadv_init(zns);
#endif
hash_clean(zns->rules_hash, zebra_pbr_rules_free);
hash_free(zns->rules_hash);
+ hash_clean(zns->ipset_hash, zebra_pbr_ipset_free);
+ hash_free(zns->ipset_hash);
+ hash_clean(zns->ipset_entry_hash,
+ zebra_pbr_ipset_entry_free),
+ hash_free(zns->ipset_entry_hash);
+ hash_clean(zns->iptable_hash,
+ zebra_pbr_iptable_free);
+ hash_free(zns->iptable_hash);
+
while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) {
znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables);
int zebra_ns_init(void)
{
ns_id_t ns_id;
+ ns_id_t ns_id_external;
dzns = zebra_ns_alloc();
ns_id = zebra_ns_id_get_default();
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
-
- ns_init_management(ns_id);
+ ns_id_external = ns_map_nsid_with_external(ns_id, true);
+ ns_init_management(ns_id_external, ns_id);
logicalrouter_init(logicalrouter_config_write);
zebra_vrf_init();
/* Default NS is activated */
- zebra_ns_enable(ns_id, (void **)&dzns);
+ zebra_ns_enable(ns_id_external, (void **)&dzns);
if (vrf_is_backend_netns()) {
ns_add_hook(NS_NEW_HOOK, zebra_ns_new);
struct hash *rules_hash;
+ struct hash *ipset_hash;
+
+ struct hash *ipset_entry_hash;
+
+ struct hash *iptable_hash;
+
/* Back pointer */
struct ns *ns;
};
uint32_t key;
rule = (struct zebra_pbr_rule *)arg;
- key = jhash_3words(rule->seq, rule->priority, rule->action.table,
- prefix_hash_key(&rule->filter.src_ip));
+ key = jhash_3words(rule->rule.seq, rule->rule.priority,
+ rule->rule.action.table,
+ prefix_hash_key(&rule->rule.filter.src_ip));
if (rule->ifp)
key = jhash_1word(rule->ifp->ifindex, key);
else
key = jhash_1word(0, key);
- return jhash_3words(rule->filter.src_port, rule->filter.dst_port,
- prefix_hash_key(&rule->filter.dst_ip),
- jhash_1word(rule->unique, key));
+ if (rule->rule.filter.fwmark)
+ key = jhash_1word(rule->rule.filter.fwmark, key);
+ else
+ key = jhash_1word(0, key);
+ return jhash_3words(rule->rule.filter.src_port,
+ rule->rule.filter.dst_port,
+ prefix_hash_key(&rule->rule.filter.dst_ip),
+ jhash_1word(rule->rule.unique, key));
}
int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
r1 = (const struct zebra_pbr_rule *)arg1;
r2 = (const struct zebra_pbr_rule *)arg2;
- if (r1->seq != r2->seq)
+ if (r1->rule.seq != r2->rule.seq)
return 0;
- if (r1->priority != r2->priority)
+ if (r1->rule.priority != r2->rule.priority)
return 0;
- if (r1->unique != r2->unique)
+ if (r1->rule.unique != r2->rule.unique)
return 0;
- if (r1->action.table != r2->action.table)
+ if (r1->rule.action.table != r2->rule.action.table)
return 0;
- if (r1->filter.src_port != r2->filter.src_port)
+ if (r1->rule.filter.src_port != r2->rule.filter.src_port)
return 0;
- if (r1->filter.dst_port != r2->filter.dst_port)
+ if (r1->rule.filter.dst_port != r2->rule.filter.dst_port)
return 0;
- if (!prefix_same(&r1->filter.src_ip, &r2->filter.src_ip))
+ if (r1->rule.filter.fwmark != r2->rule.filter.fwmark)
return 0;
- if (!prefix_same(&r1->filter.dst_ip, &r2->filter.dst_ip))
+ if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip))
+ return 0;
+
+ if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip))
return 0;
if (r1->ifp != r2->ifp)
return 1;
}
-struct pbr_unique_lookup {
+struct pbr_rule_unique_lookup {
struct zebra_pbr_rule *rule;
uint32_t unique;
+ struct interface *ifp;
};
static int pbr_rule_lookup_unique_walker(struct hash_backet *b, void *data)
{
- struct pbr_unique_lookup *pul = data;
+ struct pbr_rule_unique_lookup *pul = data;
struct zebra_pbr_rule *rule = b->data;
- if (pul->unique == rule->unique) {
+ if (pul->unique == rule->rule.unique && pul->ifp == rule->ifp) {
pul->rule = rule;
return HASHWALK_ABORT;
}
}
static struct zebra_pbr_rule *pbr_rule_lookup_unique(struct zebra_ns *zns,
- uint32_t unique)
+ uint32_t unique,
+ struct interface *ifp)
{
- struct pbr_unique_lookup pul;
+ struct pbr_rule_unique_lookup pul;
pul.unique = unique;
+ pul.ifp = ifp;
pul.rule = NULL;
hash_walk(zns->rules_hash, &pbr_rule_lookup_unique_walker, &pul);
return pul.rule;
}
+void zebra_pbr_ipset_free(void *arg)
+{
+ struct zebra_pbr_ipset *ipset;
+
+ ipset = (struct zebra_pbr_ipset *)arg;
+
+ XFREE(MTYPE_TMP, ipset);
+}
+
+uint32_t zebra_pbr_ipset_hash_key(void *arg)
+{
+ struct zebra_pbr_ipset *ipset = (struct zebra_pbr_ipset *)arg;
+ uint32_t *pnt = (uint32_t *)&ipset->ipset_name;
+
+ return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 0x63ab42de);
+}
+
+int zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_ipset *r1, *r2;
+
+ r1 = (const struct zebra_pbr_ipset *)arg1;
+ r2 = (const struct zebra_pbr_ipset *)arg2;
+
+ if (r1->type != r2->type)
+ return 0;
+ if (r1->unique != r2->unique)
+ return 0;
+ if (strncmp(r1->ipset_name, r2->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE))
+ return 0;
+ return 1;
+}
+
+void zebra_pbr_ipset_entry_free(void *arg)
+{
+ struct zebra_pbr_ipset_entry *ipset;
+
+ ipset = (struct zebra_pbr_ipset_entry *)arg;
+
+ XFREE(MTYPE_TMP, ipset);
+}
+
+uint32_t zebra_pbr_ipset_entry_hash_key(void *arg)
+{
+ struct zebra_pbr_ipset_entry *ipset;
+ uint32_t key;
+
+ ipset = (struct zebra_pbr_ipset_entry *)arg;
+ key = prefix_hash_key(&ipset->src);
+ key = jhash_1word(ipset->unique, key);
+ key = jhash_1word(prefix_hash_key(&ipset->dst), key);
+
+ return key;
+}
+
+int zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_ipset_entry *r1, *r2;
+
+ r1 = (const struct zebra_pbr_ipset_entry *)arg1;
+ r2 = (const struct zebra_pbr_ipset_entry *)arg2;
+
+ if (r1->unique != r2->unique)
+ return 0;
+
+ if (!prefix_same(&r1->src, &r2->src))
+ return 0;
+
+ if (!prefix_same(&r1->dst, &r2->dst))
+ return 0;
+
+ return 1;
+}
+
+void zebra_pbr_iptable_free(void *arg)
+{
+ struct zebra_pbr_iptable *iptable;
+
+ iptable = (struct zebra_pbr_iptable *)arg;
+
+ XFREE(MTYPE_TMP, iptable);
+}
+
+uint32_t zebra_pbr_iptable_hash_key(void *arg)
+{
+ struct zebra_pbr_iptable *iptable = (struct zebra_pbr_iptable *)arg;
+ uint32_t *pnt = (uint32_t *)&(iptable->ipset_name);
+ uint32_t key;
+
+ key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE,
+ 0x63ab42de);
+ key = jhash_1word(iptable->fwmark, key);
+ return jhash_3words(iptable->filter_bm, iptable->type,
+ iptable->unique, key);
+}
+
+int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_iptable *r1, *r2;
+
+ r1 = (const struct zebra_pbr_iptable *)arg1;
+ r2 = (const struct zebra_pbr_iptable *)arg2;
+
+ if (r1->type != r2->type)
+ return 0;
+ if (r1->unique != r2->unique)
+ return 0;
+ if (r1->filter_bm != r2->filter_bm)
+ return 0;
+ if (r1->fwmark != r2->fwmark)
+ return 0;
+ if (r1->action != r2->action)
+ return 0;
+ if (strncmp(r1->ipset_name, r2->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE))
+ return 0;
+ return 1;
+}
+
static void *pbr_rule_alloc_intern(void *arg)
{
struct zebra_pbr_rule *zpr;
void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule)
{
struct zebra_pbr_rule *unique =
- pbr_rule_lookup_unique(zns, rule->unique);
+ pbr_rule_lookup_unique(zns, rule->rule.unique, rule->ifp);
(void)hash_get(zns->rules_hash, rule, pbr_rule_alloc_intern);
kernel_add_pbr_rule(rule);
hash_iterate(zns->rules_hash, zebra_pbr_cleanup_rules, &sock);
}
+static void *pbr_ipset_alloc_intern(void *arg)
+{
+ struct zebra_pbr_ipset *zpi;
+ struct zebra_pbr_ipset *new;
+
+ zpi = (struct zebra_pbr_ipset *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_create_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset)
+{
+ (void)hash_get(zns->ipset_hash, ipset, pbr_ipset_alloc_intern);
+ /* TODO:
+ * - Netlink call
+ */
+}
+
+void zebra_pbr_destroy_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset)
+{
+ struct zebra_pbr_ipset *lookup;
+
+ lookup = hash_lookup(zns->ipset_hash, ipset);
+ /* TODO:
+ * - Netlink destroy from kernel
+ * - ?? destroy ipset entries before
+ */
+ if (lookup)
+ XFREE(MTYPE_TMP, lookup);
+ else
+ zlog_warn("%s: IPSet Entry being deleted we know nothing about",
+ __PRETTY_FUNCTION__);
+}
+
+struct pbr_ipset_name_lookup {
+ struct zebra_pbr_ipset *ipset;
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
+};
+
+static int zebra_pbr_ipset_pername_walkcb(struct hash_backet *backet, void *arg)
+{
+ struct pbr_ipset_name_lookup *pinl =
+ (struct pbr_ipset_name_lookup *)arg;
+ struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)backet->data;
+
+ if (!strncmp(pinl->ipset_name, zpi->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE)) {
+ pinl->ipset = zpi;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(struct zebra_ns *zns,
+ char *ipsetname)
+{
+ struct pbr_ipset_name_lookup pinl;
+ struct pbr_ipset_name_lookup *ptr = &pinl;
+
+ if (!ipsetname)
+ return NULL;
+ memset(ptr, 0, sizeof(struct pbr_ipset_name_lookup));
+ snprintf((char *)ptr->ipset_name, ZEBRA_IPSET_NAME_SIZE, "%s",
+ ipsetname);
+ hash_walk(zns->ipset_hash, zebra_pbr_ipset_pername_walkcb, ptr);
+ return ptr->ipset;
+}
+
+static void *pbr_ipset_entry_alloc_intern(void *arg)
+{
+ struct zebra_pbr_ipset_entry *zpi;
+ struct zebra_pbr_ipset_entry *new;
+
+ zpi = (struct zebra_pbr_ipset_entry *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset_entry));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_add_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset)
+{
+ (void)hash_get(zns->ipset_entry_hash, ipset,
+ pbr_ipset_entry_alloc_intern);
+ /* TODO:
+ * - attach to ipset list
+ * - Netlink add to kernel
+ */
+}
+
+void zebra_pbr_del_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset)
+{
+ struct zebra_pbr_ipset_entry *lookup;
+
+ lookup = hash_lookup(zns->ipset_entry_hash, ipset);
+ /* TODO:
+ * - Netlink destroy
+ * - detach from ipset list
+ * - ?? if no more entres, delete ipset
+ */
+ if (lookup)
+ XFREE(MTYPE_TMP, lookup);
+ else
+ zlog_warn("%s: IPSet being deleted we know nothing about",
+ __PRETTY_FUNCTION__);
+}
+
+static void *pbr_iptable_alloc_intern(void *arg)
+{
+ struct zebra_pbr_iptable *zpi;
+ struct zebra_pbr_iptable *new;
+
+ zpi = (struct zebra_pbr_iptable *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_add_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable)
+{
+ (void)hash_get(zns->iptable_hash, iptable,
+ pbr_iptable_alloc_intern);
+ /* TODO call netlink layer */
+}
+
+void zebra_pbr_del_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable)
+{
+ struct zebra_pbr_ipset_entry *lookup;
+
+ lookup = hash_lookup(zns->iptable_hash, iptable);
+ /* TODO:
+ * - call netlink layer
+ * - detach from iptable list
+ */
+ if (lookup)
+ XFREE(MTYPE_TMP, lookup);
+ else
+ zlog_warn("%s: IPTable being deleted we know nothing about",
+ __PRETTY_FUNCTION__);
+}
+
/*
* Handle success or failure of rule (un)install in the kernel.
*/
zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_INSTALL);
break;
case SOUTHBOUND_DELETE_SUCCESS:
+ zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED);
+ break;
+ case SOUTHBOUND_DELETE_FAILURE:
+ zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED);
+ break;
+ }
+}
+
+/*
+ * Handle success or failure of ipset (un)install in the kernel.
+ */
+void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset,
+ enum southbound_results res)
+{
+ switch (res) {
+ case SOUTHBOUND_INSTALL_SUCCESS:
+ zsend_ipset_notify_owner(ipset, ZAPI_IPSET_INSTALLED);
+ break;
+ case SOUTHBOUND_INSTALL_FAILURE:
+ zsend_ipset_notify_owner(ipset, ZAPI_IPSET_FAIL_INSTALL);
+ break;
+ case SOUTHBOUND_DELETE_SUCCESS:
+ case SOUTHBOUND_DELETE_FAILURE:
+ /* TODO : handling of delete event */
+ break;
+ }
+}
+
+/*
+ * Handle success or failure of ipset (un)install in the kernel.
+ */
+void kernel_pbr_ipset_entry_add_del_status(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum southbound_results res)
+{
+ switch (res) {
+ case SOUTHBOUND_INSTALL_SUCCESS:
+ zsend_ipset_entry_notify_owner(ipset,
+ ZAPI_IPSET_ENTRY_INSTALLED);
+ break;
+ case SOUTHBOUND_INSTALL_FAILURE:
+ zsend_ipset_entry_notify_owner(ipset,
+ ZAPI_IPSET_ENTRY_FAIL_INSTALL);
+ break;
+ case SOUTHBOUND_DELETE_SUCCESS:
+ case SOUTHBOUND_DELETE_FAILURE:
+ /* TODO : handling of delete event */
break;
+ }
+}
+
+/*
+ * Handle success or failure of ipset (un)install in the kernel.
+ */
+void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable,
+ enum southbound_results res)
+{
+ switch (res) {
+ case SOUTHBOUND_INSTALL_SUCCESS:
+ zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_INSTALLED);
+ break;
+ case SOUTHBOUND_INSTALL_FAILURE:
+ zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_FAIL_INSTALL);
+ break;
+ case SOUTHBOUND_DELETE_SUCCESS:
case SOUTHBOUND_DELETE_FAILURE:
+ /* TODO : handling of delete event */
break;
}
}
#include "if.h"
#include "rt.h"
+#include "pbr.h"
-/*
- * A PBR filter
- *
- * The filter or match criteria in a PBR rule.
- * For simplicity, all supported filters are grouped into a structure rather
- * than delineating further. A bitmask denotes which filters are actually
- * specified.
- */
-struct zebra_pbr_filter {
- uint32_t filter_bm;
-#define PBR_FILTER_SRC_IP (1 << 0)
-#define PBR_FILTER_DST_IP (1 << 1)
-#define PBR_FILTER_SRC_PORT (1 << 2)
-#define PBR_FILTER_DST_PORT (1 << 3)
-
- /* Source and Destination IP address with masks. */
- struct prefix src_ip;
- struct prefix dst_ip;
-
- /* Source and Destination higher-layer (TCP/UDP) port numbers. */
- uint16_t src_port;
- uint16_t dst_port;
+struct zebra_pbr_rule {
+ int sock;
+
+ struct pbr_rule rule;
+
+ struct interface *ifp;
};
#define IS_RULE_FILTERING_ON_SRC_IP(r) \
- (r->filter.filter_bm & PBR_FILTER_SRC_IP)
+ (r->rule.filter.filter_bm & PBR_FILTER_SRC_IP)
#define IS_RULE_FILTERING_ON_DST_IP(r) \
- (r->filter.filter_bm & PBR_FILTER_DST_IP)
+ (r->rule.filter.filter_bm & PBR_FILTER_DST_IP)
#define IS_RULE_FILTERING_ON_SRC_PORT(r) \
- (r->filter.filter_bm & PBR_FILTER_SRC_PORT)
+ (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT)
#define IS_RULE_FILTERING_ON_DST_PORT(r) \
- (r->filter.filter_bm & PBR_FILTER_DST_PORT)
+ (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT)
/*
- * A PBR action
+ * An IPSet Entry Filter
*
- * The action corresponding to a PBR rule.
- * While the user specifies the action in a particular way, the forwarding
- * plane implementation (Linux only) requires that to be encoded into a
- * route table and the rule then point to that route table; in some cases,
- * the user criteria may directly point to a table too.
+ * This is a filter mapped on ipset entries
*/
-struct zebra_pbr_action {
- uint32_t table;
+struct zebra_pbr_ipset {
+ /*
+ * Originating zclient sock fd, so we can know who to send
+ * back to.
+ */
+ int sock;
+
+ uint32_t unique;
+
+ /* type is encoded as uint32_t
+ * but value is an enum ipset_type
+ */
+ uint32_t type;
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
/*
- * A PBR rule
+ * An IPSet Entry Filter
*
- * This is a combination of the filter criteria and corresponding action.
- * Rules also have a user-defined sequence number which defines the relative
- * order amongst rules.
+ * This is a filter mapped on ipset entries
*/
-struct zebra_pbr_rule {
+struct zebra_pbr_ipset_entry {
/*
* Originating zclient sock fd, so we can know who to send
* back to.
*/
int sock;
- uint32_t seq;
- uint32_t priority;
- struct interface *ifp;
uint32_t unique;
- struct zebra_pbr_filter filter;
- struct zebra_pbr_action action;
+
+ struct prefix src;
+ struct prefix dst;
+
+ uint32_t filter_bm;
+
+ struct zebra_pbr_ipset *backpointer;
+};
+
+/*
+ * An IPTables Action
+ *
+ * This is a filter mapped on ipset entries
+ */
+struct zebra_pbr_iptable {
+ /*
+ * Originating zclient sock fd, so we can know who to send
+ * back to.
+ */
+ int sock;
+
+ uint32_t unique;
+
+ /* include ipset type
+ */
+ uint32_t type;
+
+ /* include which IP is to be filtered
+ */
+ uint32_t filter_bm;
+
+ uint32_t fwmark;
+
+ uint32_t action;
+
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule);
void zebra_pbr_del_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule);
+void zebra_pbr_create_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset);
+void zebra_pbr_destroy_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset);
+struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(struct zebra_ns *zns,
+ char *ipsetname);
+void zebra_pbr_add_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset);
+void zebra_pbr_del_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset);
+
+void zebra_pbr_add_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable);
+void zebra_pbr_del_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable);
/*
* Install specified rule for a specific interface.
extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule,
enum southbound_results res);
+/*
+ * Handle success or failure of ipset kinds (un)install in the kernel.
+ */
+extern void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset,
+ enum southbound_results res);
+
+extern void kernel_pbr_ipset_entry_add_del_status(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum southbound_results res);
+
+extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable,
+ enum southbound_results res);
+
/*
* Handle rule delete notification from kernel.
*/
extern uint32_t zebra_pbr_rules_hash_key(void *arg);
extern int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2);
+/* has operates on 32bit pointer
+ * and field is a string of 8bit
+ */
+#define ZEBRA_IPSET_NAME_HASH_SIZE (ZEBRA_IPSET_NAME_SIZE / 4)
+
+extern void zebra_pbr_ipset_free(void *arg);
+extern uint32_t zebra_pbr_ipset_hash_key(void *arg);
+extern int zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2);
+
+extern void zebra_pbr_ipset_entry_free(void *arg);
+extern uint32_t zebra_pbr_ipset_entry_hash_key(void *arg);
+extern int zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2);
+
+extern void zebra_pbr_iptable_free(void *arg);
+extern uint32_t zebra_pbr_iptable_hash_key(void *arg);
+extern int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2);
+
#endif /* _ZEBRA_PBR_H */
* just rethink it. Yes this is a hammer, but
* a small one
*/
- if (o_re)
+ if (o_re) {
+ SET_FLAG(o_re->status, ROUTE_ENTRY_CHANGED);
rib_queue_add(o_rn);
+ }
}
}
}
struct zserv *client;
struct stream *s;
- if (IS_ZEBRA_DEBUG_PACKET) {
+ if (IS_ZEBRA_DEBUG_PACKET)
zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
- rule->unique);
- }
+ rule->rule.unique);
for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
if (rule->sock == client->sock)
return;
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
- stream_reset(s);
zclient_create_header(s, ZEBRA_RULE_NOTIFY_OWNER, VRF_DEFAULT);
stream_put(s, ¬e, sizeof(note));
- stream_putl(s, rule->seq);
- stream_putl(s, rule->priority);
- stream_putl(s, rule->unique);
+ stream_putl(s, rule->rule.seq);
+ stream_putl(s, rule->rule.priority);
+ stream_putl(s, rule->rule.unique);
if (rule->ifp)
stream_putl(s, rule->ifp->ifindex);
else
zebra_server_send_message(client, s);
}
+void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset,
+ enum zapi_ipset_notify_owner note)
+{
+ struct listnode *node;
+ struct zserv *client;
+ struct stream *s;
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
+ ipset->unique);
+
+ for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
+ if (ipset->sock == client->sock)
+ break;
+ }
+
+ if (!client)
+ return;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_IPSET_NOTIFY_OWNER, VRF_DEFAULT);
+ stream_put(s, ¬e, sizeof(note));
+ stream_putl(s, ipset->unique);
+ stream_put(s, ipset->ipset_name, ZEBRA_IPSET_NAME_SIZE);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zebra_server_send_message(client, s);
+}
+
+void zsend_ipset_entry_notify_owner(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum zapi_ipset_entry_notify_owner note)
+{
+ struct listnode *node;
+ struct zserv *client;
+ struct stream *s;
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
+ ipset->unique);
+
+ for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
+ if (ipset->sock == client->sock)
+ break;
+ }
+
+ if (!client)
+ return;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_IPSET_ENTRY_NOTIFY_OWNER,
+ VRF_DEFAULT);
+ stream_put(s, ¬e, sizeof(note));
+ stream_putl(s, ipset->unique);
+ stream_put(s, ipset->backpointer->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zebra_server_send_message(client, s);
+}
+
+void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable,
+ enum zapi_iptable_notify_owner note)
+{
+ struct listnode *node;
+ struct zserv *client;
+ struct stream *s;
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
+ iptable->unique);
+
+ for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
+ if (iptable->sock == client->sock)
+ break;
+ }
+
+ if (!client)
+ return;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_IPTABLE_NOTIFY_OWNER, VRF_DEFAULT);
+ stream_put(s, ¬e, sizeof(note));
+ stream_putl(s, iptable->unique);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zebra_server_send_message(client, s);
+}
+
/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */
int zsend_router_id_update(struct zserv *client, struct prefix *p,
vrf_id_t vrf_id)
memset(&zpr, 0, sizeof(zpr));
zpr.sock = client->sock;
- STREAM_GETL(s, zpr.seq);
- STREAM_GETL(s, zpr.priority);
- STREAM_GETL(s, zpr.unique);
- STREAM_GETC(s, zpr.filter.src_ip.family);
- STREAM_GETC(s, zpr.filter.src_ip.prefixlen);
- STREAM_GET(&zpr.filter.src_ip.u.prefix, s,
- prefix_blen(&zpr.filter.src_ip));
- STREAM_GETW(s, zpr.filter.src_port);
- STREAM_GETC(s, zpr.filter.dst_ip.family);
- STREAM_GETC(s, zpr.filter.dst_ip.prefixlen);
- STREAM_GET(&zpr.filter.dst_ip.u.prefix, s,
- prefix_blen(&zpr.filter.dst_ip));
- STREAM_GETW(s, zpr.filter.dst_port);
- STREAM_GETL(s, zpr.action.table);
+ zpr.rule.vrf_id = hdr->vrf_id;
+ STREAM_GETL(s, zpr.rule.seq);
+ STREAM_GETL(s, zpr.rule.priority);
+ STREAM_GETL(s, zpr.rule.unique);
+ STREAM_GETC(s, zpr.rule.filter.src_ip.family);
+ STREAM_GETC(s, zpr.rule.filter.src_ip.prefixlen);
+ STREAM_GET(&zpr.rule.filter.src_ip.u.prefix, s,
+ prefix_blen(&zpr.rule.filter.src_ip));
+ STREAM_GETW(s, zpr.rule.filter.src_port);
+ STREAM_GETC(s, zpr.rule.filter.dst_ip.family);
+ STREAM_GETC(s, zpr.rule.filter.dst_ip.prefixlen);
+ STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s,
+ prefix_blen(&zpr.rule.filter.dst_ip));
+ STREAM_GETW(s, zpr.rule.filter.dst_port);
+ STREAM_GETL(s, zpr.rule.filter.fwmark);
+ STREAM_GETL(s, zpr.rule.action.table);
STREAM_GETL(s, ifindex);
- zpr.ifp = if_lookup_by_index(ifindex, VRF_UNKNOWN);
- if (!zpr.ifp) {
- zlog_debug("FAiled to lookup ifindex: %u", ifindex);
- return;
+ if (ifindex) {
+ zpr.ifp = if_lookup_by_index(ifindex, VRF_UNKNOWN);
+ if (!zpr.ifp) {
+ zlog_debug("Failed to lookup ifindex: %u",
+ ifindex);
+ return;
+ }
}
- if (!is_default_prefix(&zpr.filter.src_ip))
- zpr.filter.filter_bm |= PBR_FILTER_SRC_IP;
+ if (!is_default_prefix(&zpr.rule.filter.src_ip))
+ zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
+
+ if (!is_default_prefix(&zpr.rule.filter.dst_ip))
+ zpr.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
- if (!is_default_prefix(&zpr.filter.dst_ip))
- zpr.filter.filter_bm |= PBR_FILTER_DST_IP;
+ if (zpr.rule.filter.src_port)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_PORT;
- if (zpr.filter.src_port)
- zpr.filter.filter_bm |= PBR_FILTER_SRC_PORT;
+ if (zpr.rule.filter.dst_port)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT;
- if (zpr.filter.dst_port)
- zpr.filter.filter_bm |= PBR_FILTER_DST_PORT;
+ if (zpr.rule.filter.fwmark)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
if (hdr->command == ZEBRA_RULE_ADD)
zebra_pbr_add_rule(zvrf->zns, &zpr);
return;
}
+
+static inline void zread_ipset(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_pbr_ipset zpi;
+ struct stream *s;
+ uint32_t total, i;
+
+ s = msg;
+ STREAM_GETL(s, total);
+
+ for (i = 0; i < total; i++) {
+ memset(&zpi, 0, sizeof(zpi));
+
+ zpi.sock = client->sock;
+ STREAM_GETL(s, zpi.unique);
+ STREAM_GETL(s, zpi.type);
+ STREAM_GET(&zpi.ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ if (hdr->command == ZEBRA_IPSET_CREATE)
+ zebra_pbr_create_ipset(zvrf->zns, &zpi);
+ else
+ zebra_pbr_destroy_ipset(zvrf->zns, &zpi);
+ }
+
+stream_failure:
+ return;
+}
+
+static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_pbr_ipset_entry zpi;
+ struct zebra_pbr_ipset ipset;
+ struct stream *s;
+ uint32_t total, i;
+
+ s = msg;
+ STREAM_GETL(s, total);
+
+ for (i = 0; i < total; i++) {
+ memset(&zpi, 0, sizeof(zpi));
+ memset(&ipset, 0, sizeof(ipset));
+
+ zpi.sock = client->sock;
+ STREAM_GETL(s, zpi.unique);
+ STREAM_GET(&ipset.ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+ STREAM_GETC(s, zpi.src.family);
+ STREAM_GETC(s, zpi.src.prefixlen);
+ STREAM_GET(&zpi.src.u.prefix, s,
+ prefix_blen(&zpi.src));
+ STREAM_GETC(s, zpi.dst.family);
+ STREAM_GETC(s, zpi.dst.prefixlen);
+ STREAM_GET(&zpi.dst.u.prefix, s,
+ prefix_blen(&zpi.dst));
+
+ if (!is_default_prefix(&zpi.src))
+ zpi.filter_bm |= PBR_FILTER_SRC_IP;
+
+ if (!is_default_prefix(&zpi.dst))
+ zpi.filter_bm |= PBR_FILTER_DST_IP;
+
+ /* calculate backpointer */
+ zpi.backpointer = zebra_pbr_lookup_ipset_pername(zvrf->zns,
+ ipset.ipset_name);
+ if (hdr->command == ZEBRA_IPSET_ENTRY_ADD)
+ zebra_pbr_add_ipset_entry(zvrf->zns, &zpi);
+ else
+ zebra_pbr_del_ipset_entry(zvrf->zns, &zpi);
+ }
+
+stream_failure:
+ return;
+}
+
+static inline void zread_iptable(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_pbr_iptable zpi;
+ struct stream *s;
+
+ s = msg;
+
+ memset(&zpi, 0, sizeof(zpi));
+
+ zpi.sock = client->sock;
+ STREAM_GETL(s, zpi.unique);
+ STREAM_GETL(s, zpi.type);
+ STREAM_GETL(s, zpi.filter_bm);
+ STREAM_GETL(s, zpi.action);
+ STREAM_GETL(s, zpi.fwmark);
+ STREAM_GET(&zpi.ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ if (hdr->command == ZEBRA_IPTABLE_ADD)
+ zebra_pbr_add_iptable(zvrf->zns, &zpi);
+ else
+ zebra_pbr_del_iptable(zvrf->zns, &zpi);
+stream_failure:
+ return;
+}
+
void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_ROUTER_ID_ADD] = zread_router_id_add,
[ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,
[ZEBRA_TABLE_MANAGER_CONNECT] = zread_table_manager_request,
[ZEBRA_GET_TABLE_CHUNK] = zread_table_manager_request,
[ZEBRA_RELEASE_TABLE_CHUNK] = zread_table_manager_request,
+ [ZEBRA_IPSET_CREATE] = zread_ipset,
+ [ZEBRA_IPSET_DESTROY] = zread_ipset,
+ [ZEBRA_IPSET_ENTRY_ADD] = zread_ipset_entry,
+ [ZEBRA_IPSET_ENTRY_DELETE] = zread_ipset_entry,
+ [ZEBRA_IPTABLE_ADD] = zread_iptable,
+ [ZEBRA_IPTABLE_DELETE] = zread_iptable,
};
static inline void zserv_handle_commands(struct zserv *client,
#include "routemap.h"
#include "vty.h"
#include "zclient.h"
+#include "pbr.h"
#include "zebra/zebra_ns.h"
#include "zebra/zebra_pw.h"
extern int zsend_route_notify_owner(struct route_entry *re, struct prefix *p,
enum zapi_route_notify_owner note);
+struct zebra_pbr_ipset;
+struct zebra_pbr_ipset_entry;
+struct zebra_pbr_iptable;
struct zebra_pbr_rule;
extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
enum zapi_rule_notify_owner note);
+extern void zsend_ipset_notify_owner(
+ struct zebra_pbr_ipset *ipset,
+ enum zapi_ipset_notify_owner note);
+extern void zsend_ipset_entry_notify_owner(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum zapi_ipset_entry_notify_owner note);
+extern void zsend_iptable_notify_owner(
+ struct zebra_pbr_iptable *iptable,
+ enum zapi_iptable_notify_owner note);
extern void zserv_nexthop_num_warn(const char *, const struct prefix *,
const unsigned int);