Merge branch 'master' into docuser

* New manpage: mtracebis.rst
* Makefile.am includes mtracebis.rst
* configure.ac lines removed
* Debian packaging files updated
* Fixed up manpage |seealso-programs| in the process
* Centos7 build package list updated to include systemd-devel
* New paragraph on netns vrf support in zebra manpage

Conflicts:
	configure.ac
	debianpkg/backports/ubuntu14.04/debian/frr.install
	debianpkg/frr.install
	doc/Makefile.am
	doc/developer/Building_FRR_on_CentOS7.rst
	doc/zebra.8.in

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2018-03-01 11:10:01 -05:00
commit a975744835
No known key found for this signature in database
GPG Key ID: DAF48E0F57E0834F
144 changed files with 6156 additions and 1693 deletions

View File

@ -492,6 +492,36 @@ In all cases, compatibility pieces should be marked with compiler/preprocessor
annotations to print warnings at compile time, pointing to the appropriate
update path. A `-Werror` build should fail if compatibility bits are used.
### Release Process/Schedule
FRR employs a <MAJOR>.<MINOR>.<BUGFIX> versioning scheme.
* MAJOR - Significant new features or multiple minor features
A example of a MAJOR feature is a New Routing Protocol
* MINOR - Smaller Features
A example of a MINOR feature is the addition of the BGP Shutdown feature.
* BUGFIX - Fixes for actual bugs and/or security issues.
We will pull a new development branch for the next release every 4 months.
The current schedule is Feb/June/October 1. The decision for a MAJOR/MINOR
release is made at the time of branch pull based on what has been received
the previous 4 months. The branch name will be dev/MAJOR.MINOR. At
this point in time the master branch configure.ac and packaging systems
will be updated to reflect the next possible release name to allow
for easy distinguishing. Additionally the new dev branch will have
these files updated too.
After one month the development branch will be renamed to
stable/MAJOR.MINOR. This process is not held up unless a crash or
security issue has been found and needs to be addressed. Issues
being fixed will not cause a delay.
Bug fix releases are at 1 month intervals until next MAJOR.MINOR is
pulled. Then at that time as needed for issues filed.
Security issues are fixed for 1 year minimum on old releases and
normal bug fixes for the current and previous release
### Miscellaneous
When in doubt, follow the guidelines in the Linux kernel style guide, or ask on

View File

@ -465,7 +465,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer,
}
if (peer->su.sa.sa_family == AF_INET) {
stream_putw(obuf, peer->ifindex);
stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
stream_putw(obuf, AFI_IP);
stream_put(obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
@ -477,7 +477,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer,
stream_put(obuf, empty, IPV4_MAX_BYTELEN);
} else if (peer->su.sa.sa_family == AF_INET6) {
/* Interface Index and Address family. */
stream_putw(obuf, peer->ifindex);
stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
stream_putw(obuf, AFI_IP6);
/* Source IP Address and Destination IP Address. */

View File

@ -666,10 +666,11 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom);
/* Add the export RTs for L3VNI - currently only supported for IPV4 host
* routes
/*
* only attach l3-vni export rts for ipv4 address family and if we are
* advertising both the labels in type-2 routes
*/
if (afi == AFI_IP) {
if (afi == AFI_IP && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
@ -690,7 +691,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
ecommunity_merge(attr->ecommunity, &ecom_sticky);
}
if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) {
/*
* only attach l3-vni rmac for ipv4 address family and if we are
* advertising both the labels in type-2 routes
*/
if (afi == AFI_IP && !is_zero_mac(&attr->rmac) &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
memset(&ecom_rmac, 0, sizeof(ecom_rmac));
encode_rmac_extcomm(&eval_rmac, &attr->rmac);
ecom_rmac.size = 1;
@ -1189,8 +1195,13 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
/* The VNI goes into the 'label' field of the route */
vni2label(vpn->vni, &label[0]);
/* Type-2 routes may carry a second VNI - the L3-VNI */
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
/* Type-2 routes may carry a second VNI - the L3-VNI.
* Only attach second label if we are advertising two labels for
* type-2 routes.
*/
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vni_t l3vni;
l3vni = bgpevpn_get_l3vni(vpn);
@ -1209,6 +1220,24 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
&& !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
route_change = 0;
else {
/*
* The attributes have changed, type-2 routes needs to
* be advertised with right labels.
*/
vni2label(vpn->vni, &label[0]);
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vni_t l3vni;
l3vni = bgpevpn_get_l3vni(vpn);
if (l3vni) {
vni2label(l3vni, &label[1]);
num_labels++;
}
}
memcpy(&tmp_ri->extra->label, label, sizeof(label));
tmp_ri->extra->num_labels = num_labels;
/* The attribute has changed. */
/* Add (or update) attribute to hash. */
attr_new = bgp_attr_intern(attr);
@ -1254,8 +1283,14 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0;
attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0;
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL);
bgpevpn_get_rmac(vpn, &attr.rmac);
/* PMSI is only needed for type-3 routes */
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE)
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL);
/* router mac is only needed for type-2 and type-5 routes */
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
bgpevpn_get_rmac(vpn, &attr.rmac);
vni2label(vpn->vni, &(attr.label));
/* Set up RT and ENCAP extended community. */
@ -3275,6 +3310,19 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
*/
for (ri = rn->info; ri; ri = ri->next) {
if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)) {
/* apply the route-map */
if (bgp_vrf->adv_cmd_rmap[afi][safi].map) {
int ret = 0;
ret =
route_map_apply(
bgp_vrf->adv_cmd_rmap[afi][safi].map,
&rn->p, RMAP_BGP, ri);
if (ret == RMAP_DENYMATCH)
continue;
}
bgp_evpn_advertise_type5_route(bgp_vrf, &rn->p,
ri->attr,
afi, safi);
@ -4214,7 +4262,8 @@ static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
int bgp_evpn_local_l3vni_add(vni_t l3vni,
vrf_id_t vrf_id,
struct ethaddr *rmac,
struct in_addr originator_ip)
struct in_addr originator_ip,
int filter)
{
struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
struct bgp *bgp_def = NULL; /* default bgp instance */
@ -4266,6 +4315,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
/* set the originator ip */
bgp_vrf->originator_ip = originator_ip;
/* set the right filter - are we using l3vni only for prefix routes? */
if (filter)
SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
/* auto derive RD/RT */
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
@ -4279,9 +4332,12 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
link_l2vni_hash_to_l3vni,
bgp_vrf);
/* updates all corresponding local mac-ip routes */
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
/* Only update all corresponding type-2 routes if we are advertising two
* labels along with type-2 routes
*/
if (!filter)
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
/* advertise type-5 routes if needed */
update_advertise_vrf_routes(bgp_vrf);
@ -4340,9 +4396,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni,
}
/* update all corresponding local mac-ip routes */
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
update_routes_for_vni(bgp_def, vpn);
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) {
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
update_routes_for_vni(bgp_def, vpn);
}
}
/* Delete the instance if it was autocreated */
if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))

View File

@ -91,7 +91,8 @@ extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
u_char flags);
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
struct ethaddr *rmac,
struct in_addr originator_ip);
struct in_addr originator_ip,
int filter);
extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id);
extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,

View File

@ -61,6 +61,10 @@ struct bgpevpn {
#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */
#define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */
#define VNI_FLAG_USE_TWO_LABELS 0x20 /* Attach both L2-VNI and L3-VNI if
needed for this VPN */
struct bgp *bgp_vrf; /* back pointer to the vrf instance */
/* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
u_int8_t advertise_gw_macip;
@ -132,66 +136,66 @@ static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf,
static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn)
{
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf)
return 0;
return bgp_vrf->l3vni;
return vpn->bgp_vrf ? vpn->bgp_vrf->l3vni : 0;
}
static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac)
{
struct bgp *bgp_vrf = NULL;
memset(rmac, 0, sizeof(struct ethaddr));
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf)
if (!vpn->bgp_vrf)
return;
memcpy(rmac, &bgp_vrf->rmac, sizeof(struct ethaddr));
memcpy(rmac, &vpn->bgp_vrf->rmac, sizeof(struct ethaddr));
}
static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn)
{
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf)
if (!vpn->bgp_vrf)
return NULL;
return bgp_vrf->vrf_export_rtl;
return vpn->bgp_vrf->vrf_export_rtl;
}
static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn)
{
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf)
if (!vpn->bgp_vrf)
return NULL;
return bgp_vrf->vrf_import_rtl;
return vpn->bgp_vrf->vrf_import_rtl;
}
static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
{
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf || !bgp_vrf->l2vnis)
/* bail if vpn is not associated to bgp_vrf */
if (!vpn->bgp_vrf)
return;
listnode_delete(bgp_vrf->l2vnis, vpn);
UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
listnode_delete(vpn->bgp_vrf->l2vnis, vpn);
/* remove the backpointer to the vrf instance */
vpn->bgp_vrf = NULL;
}
static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
{
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf || !bgp_vrf->l2vnis)
/* bail if vpn is already associated to vrf */
if (vpn->bgp_vrf)
return;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf)
return;
/* associate the vpn to the bgp_vrf instance */
vpn->bgp_vrf = bgp_vrf;
listnode_add_sort(bgp_vrf->l2vnis, vpn);
/* check if we are advertising two labels for this vpn */
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY))
SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
}
static inline int is_vni_configured(struct bgpevpn *vpn)

View File

@ -83,7 +83,7 @@ static void display_vrf_import_rt(struct vty *vty,
case ECOMMUNITY_ENCODE_AS:
eas.as = (*pnt++ << 8);
eas.as |= (*pnt++);
pnt = ptr_get_be32(pnt, &eas.val);
ptr_get_be32(pnt, &eas.val);
snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
@ -195,7 +195,7 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt,
case ECOMMUNITY_ENCODE_AS:
eas.as = (*pnt++ << 8);
eas.as |= (*pnt++);
pnt = ptr_get_be32(pnt, &eas.val);
ptr_get_be32(pnt, &eas.val);
snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
@ -491,53 +491,6 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
json_object_object_add(json, "exportRts", json_export_rtl);
}
static void evpn_show_vrf_routes(struct vty *vty,
struct bgp *bgp_vrf)
{
struct bgp *bgp_def = NULL;
struct bgp_node *rn;
struct bgp_info *ri;
int header = 1;
u_int32_t prefix_cnt, path_cnt;
struct bgp_table *table;
prefix_cnt = path_cnt = 0;
bgp_def = bgp_get_default();
if (!bgp_def)
return;
table = (struct bgp_table *)bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
char prefix_str[BUFSIZ];
bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str,
sizeof(prefix_str));
if (rn->info) {
/* Overall header/legend displayed once. */
if (header) {
bgp_evpn_show_route_header(vty, bgp_def, NULL);
header = 0;
}
prefix_cnt++;
}
/* For EVPN, the prefix is displayed for each path (to fit in
* with code that already exists).
*/
for (ri = rn->info; ri; ri = ri->next) {
route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL);
path_cnt++;
}
}
if (prefix_cnt == 0)
vty_out(vty, "No EVPN prefixes exist for this VRF");
else
vty_out(vty, "\nDisplayed %u prefixes (%u paths)",
prefix_cnt, path_cnt);
}
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
struct vty *vty, struct in_addr vtep_ip,
json_object *json)
@ -2723,19 +2676,34 @@ DEFUN (no_bgp_evpn_advertise_vni_subnet,
DEFUN (bgp_evpn_advertise_type5,
bgp_evpn_advertise_type5_cmd,
"advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
"advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]",
"Advertise prefix routes\n"
BGP_AFI_HELP_STR
BGP_SAFI_HELP_STR)
BGP_SAFI_HELP_STR
"route-map for filtering specific routes\n"
"Name of the route map\n")
{
struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
int idx_afi = 0;
int idx_safi = 0;
int idx_rmap = 0;
afi_t afi = 0;
safi_t safi = 0;
int ret = 0;
int rmap_changed = 0;
argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
ret = argv_find(argv, argc, "route-map", &idx_rmap);
if (ret) {
if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
rmap_changed = 1;
else if (strcmp(argv[idx_rmap + 1]->arg,
bgp_vrf->adv_cmd_rmap[afi][safi].name) != 0)
rmap_changed = 1;
} else if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
rmap_changed = 1;
}
if (!(afi == AFI_IP) || (afi == AFI_IP6)) {
vty_out(vty,
@ -2754,24 +2722,44 @@ DEFUN (bgp_evpn_advertise_type5,
/* if we are already advertising ipv4 prefix as type-5
* nothing to do
*/
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
SET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
}
if (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN))
return CMD_WARNING;
SET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
} else {
/* if we are already advertising ipv6 prefix as type-5
* nothing to do
*/
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
SET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
if (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
return CMD_WARNING;
SET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
}
if (rmap_changed) {
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
XFREE(MTYPE_ROUTE_MAP_NAME,
bgp_vrf->adv_cmd_rmap[afi][safi].name);
bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
}
}
/* set the route-map for advertise command */
if (ret && argv[idx_rmap + 1]->arg) {
bgp_vrf->adv_cmd_rmap[afi][safi].name =
XSTRDUP(MTYPE_ROUTE_MAP_NAME,
argv[idx_rmap + 1]->arg);
bgp_vrf->adv_cmd_rmap[afi][safi].map =
route_map_lookup_by_name(argv[idx_rmap + 1]->arg);
}
/* advertise type-5 routes */
bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
return CMD_SUCCESS;
}
@ -2827,6 +2815,15 @@ DEFUN (no_bgp_evpn_advertise_type5,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
}
}
/* clear the route-map information for advertise ipv4/ipv6 unicast */
if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
XFREE(MTYPE_ROUTE_MAP_NAME,
bgp_vrf->adv_cmd_rmap[afi][safi].name);
bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
}
return CMD_SUCCESS;
}
@ -2923,16 +2920,23 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
*/
DEFUN(show_bgp_l2vpn_evpn_summary,
show_bgp_l2vpn_evpn_summary_cmd,
"show bgp l2vpn evpn summary [json]",
"show bgp [vrf VRFNAME] l2vpn evpn summary [json]",
SHOW_STR
BGP_STR
"bgp vrf\n"
"vrf name\n"
L2VPN_HELP_STR
EVPN_HELP_STR
"Summary of BGP neighbor status\n"
JSON_STR)
{
int idx_vrf = 0;
u_char uj = use_json(argc, argv);
return bgp_show_summary_vty(vty, NULL, AFI_L2VPN, SAFI_EVPN, uj);
char *vrf = NULL;
if (argv_find(argv, argc, "vrf", &idx_vrf))
vrf = argv[++idx_vrf]->arg;
return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, uj);
}
/*
@ -3139,33 +3143,6 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
return CMD_SUCCESS;
}
/*
* Display per-VRF EVPN routing table.
*/
DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd,
"show bgp l2vpn evpn route vrf VRFNAME",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
EVPN_HELP_STR
"EVPN route information\n"
"VRF\n"
"VRF Name\n")
{
int vrf_idx = 6;
char *vrf_name = NULL;
struct bgp *bgp_vrf = NULL;
vrf_name = argv[vrf_idx]->arg;
bgp_vrf = bgp_lookup_by_name(vrf_name);
if (!bgp_vrf)
return CMD_WARNING;
evpn_show_vrf_routes(vty, bgp_vrf);
return CMD_SUCCESS;
}
/*
* Display per-VNI EVPN routing table.
*/
@ -3897,6 +3874,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
vty_out(vty, " Rmac: %s\n",
prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
vty_out(vty, " VNI Filter: %s\n",
CHECK_FLAG(bgp->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY) ?
"prefix-routes-only" : "none");
vty_out(vty, " L2-VNI List:\n");
vty_out(vty, " ");
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
@ -3922,6 +3903,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
json_object_string_add(json, "rmac",
prefix_mac2str(&bgp->rmac, buf,
sizeof(buf)));
json_object_string_add(json, "vniFilter",
CHECK_FLAG(bgp->vrf_flags,
BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
? "prefix-routes-only" : "none");
/* list of l2vnis */
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
json_object_array_add(json_vnis,
@ -4325,6 +4310,8 @@ DEFUN (no_bgp_evpn_vni_rt_without_val,
void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
safi_t safi)
{
char buf1[RD_ADDRSTRLEN];
if (bgp->vnihash)
hash_iterate(bgp->vnihash,
(void (*)(struct hash_backet *,
@ -4342,6 +4329,42 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
vty_out(vty, " advertise ipv6 unicast\n");
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD))
vty_out(vty, " rd %s\n",
prefix_rd2str(&bgp->vrf_prd, buf1, sizeof(buf1)));
/* import route-target */
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
char *ecom_str;
struct listnode *node, *nnode;
struct ecommunity *ecom;
for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
ecom)) {
ecom_str = ecommunity_ecom2str(
ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
vty_out(vty, " route-target import %s\n",
ecom_str);
XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
}
}
/* export route-target */
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
char *ecom_str;
struct listnode *node, *nnode;
struct ecommunity *ecom;
for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
ecom)) {
ecom_str = ecommunity_ecom2str(
ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
vty_out(vty, " route-target export %s\n",
ecom_str);
XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
}
}
}
void bgp_ethernetvpn_init(void)
@ -4379,7 +4402,6 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd);
install_element(VIEW_NODE,
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);

View File

@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer)
/* First entry point of peer's finite state machine. In Idle
status start timer is on unless peer is shutdown or peer is
inactive. All other timer must be turned off */
if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)) {
if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)
|| peer->bgp->vrf_id == VRF_UNKNOWN) {
BGP_TIMER_OFF(peer->t_start);
} else {
BGP_TIMER_ON(peer->t_start, bgp_start_timer,
@ -1376,6 +1377,15 @@ int bgp_start(struct peer *peer)
return 0;
}
if (peer->bgp &&
peer->bgp->vrf_id == VRF_UNKNOWN) {
if (bgp_debug_neighbor_events(peer))
zlog_err(
"%s [FSM] In a VRF that is not initialised yet",
peer->host);
return -1;
}
/* Register to be notified on peer up */
if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
&& !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)

View File

@ -119,7 +119,7 @@ static void peer_process(struct hash_backet *hb, void *arg)
}
/* if calculated next update for this peer < current delay, use it */
if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <))
if (next_update->tv_sec < 0 || timercmp(&diff, next_update, <))
*next_update = diff;
}

View File

@ -41,6 +41,7 @@
#include "vrf.h"
#include "bfd.h"
#include "libfrr.h"
#include "ns.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
@ -57,6 +58,7 @@
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_network.h"
#ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/rfapi_backend.h"
@ -259,6 +261,7 @@ static int bgp_vrf_enable(struct vrf *vrf)
/* We have instance configured, link to VRF and make it "up". */
bgp_vrf_link(bgp, vrf);
bgp_handle_socket(bgp, vrf, old_vrf_id, true);
/* Update any redistribute vrf bitmaps if the vrf_id changed */
if (old_vrf_id != bgp->vrf_id)
bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id);
@ -282,6 +285,7 @@ static int bgp_vrf_disable(struct vrf *vrf)
bgp = bgp_lookup_by_name(vrf->name);
if (bgp) {
old_vrf_id = bgp->vrf_id;
bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
/* We have instance configured, unlink from VRF and make it
* "down". */
bgp_vrf_unlink(bgp, vrf);

View File

@ -92,6 +92,26 @@ int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
return 0;
}
/*
* bgp_interface_same
*
* Return true if ifindex for ifp1 and ifp2 are the same, else return false.
*/
static int bgp_interface_same(struct interface *ifp1, struct interface *ifp2)
{
if (!ifp1 && !ifp2)
return 1;
if (!ifp1 && ifp2)
return 0;
if (ifp1 && !ifp2)
return 0;
return (ifp1->ifindex == ifp2->ifindex);
}
/*
* bgp_info_nexthop_cmp
*
@ -130,7 +150,8 @@ int bgp_info_nexthop_cmp(struct bgp_info *bi1, struct bgp_info *bi2)
if (!bi1->attr->mp_nexthop_prefer_global &&
!bi2->attr->mp_nexthop_prefer_global)
compare = !(bi1->peer->ifindex == bi2->peer->ifindex);
compare = !bgp_interface_same(bi1->peer->ifp, bi2->peer->ifp);
if (!compare)
compare = IPV6_ADDR_CMP(&addr1, &addr2);
break;

View File

@ -34,6 +34,7 @@
#include "queue.h"
#include "hash.h"
#include "filter.h"
#include "ns.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_open.h"
@ -44,13 +45,14 @@
extern struct zebra_privs_t bgpd_privs;
static int bgp_bind(struct peer *);
static char *bgp_get_bound_name(struct peer *peer);
/* BGP listening socket. */
struct bgp_listener {
int fd;
union sockunion su;
struct thread *thread;
struct bgp *bgp;
};
/*
@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread)
return -1;
}
listener->thread = NULL;
thread_add_read(bm->master, bgp_accept, listener, accept_sock,
&listener->thread);
@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread)
}
set_nonblocking(bgp_sock);
/* Obtain BGP instance this connection is meant for. */
if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
/* Obtain BGP instance this connection is meant for.
* - if it is a VRF netns sock, then BGP is in listener structure
* - otherwise, the bgp instance need to be demultiplexed
*/
if (listener->bgp)
bgp = listener->bgp;
else if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
if (bgp_debug_neighbor_events(NULL))
zlog_debug(
"[Event] Could not get instance for incoming conn from %s",
@ -407,7 +415,7 @@ static int bgp_accept(struct thread *thread)
peer->doppelganger = peer1;
peer1->doppelganger = peer;
peer->fd = bgp_sock;
bgp_bind(peer);
vrf_bind(peer->bgp->vrf_id, bgp_sock, bgp_get_bound_name(peer));
bgp_fsm_change_status(peer, Active);
BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */
@ -435,21 +443,20 @@ static int bgp_accept(struct thread *thread)
}
/* BGP socket bind. */
static int bgp_bind(struct peer *peer)
static char *bgp_get_bound_name(struct peer *peer)
{
#ifdef SO_BINDTODEVICE
int ret;
int myerrno;
char *name = NULL;
/* If not bound to an interface or part of a VRF, we don't care. */
if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if)
return 0;
if ((peer->bgp->vrf_id == VRF_DEFAULT) &&
!peer->ifname && !peer->conf_if)
return NULL;
if (peer->su.sa.sa_family != AF_INET
&& peer->su.sa.sa_family != AF_INET6)
return 0; // unexpected
return NULL; // unexpected
if (!peer)
return name;
/* For IPv6 peering, interface (unnumbered or link-local with interface)
* takes precedence over VRF. For IPv4 peering, explicit interface or
* VRF are the situations to bind.
@ -461,30 +468,7 @@ static int bgp_bind(struct peer *peer)
else
name = peer->ifname ? peer->ifname : peer->bgp->name;
if (!name)
return 0;
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s Binding to interface %s", peer->host, name);
if (bgpd_privs.change(ZPRIVS_RAISE))
zlog_err("bgp_bind: could not raise privs");
ret = setsockopt(peer->fd, SOL_SOCKET, SO_BINDTODEVICE, name,
strlen(name));
myerrno = errno;
if (bgpd_privs.change(ZPRIVS_LOWER))
zlog_err("bgp_bind: could not lower privs");
if (ret < 0) {
if (bgp_debug_neighbor_events(peer))
zlog_debug("bind to interface %s failed, errno=%d",
name, myerrno);
return ret;
}
#endif /* SO_BINDTODEVICE */
return 0;
return name;
}
static int bgp_update_address(struct interface *ifp, const union sockunion *dst,
@ -558,8 +542,13 @@ int bgp_connect(struct peer *peer)
zlog_debug("Peer address not learnt: Returning from connect");
return 0;
}
if (bgpd_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
/* Make socket for the peer. */
peer->fd = sockunion_socket(&peer->su);
peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id,
bgp_get_bound_name(peer));
if (bgpd_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
if (peer->fd < 0)
return -1;
@ -591,9 +580,6 @@ int bgp_connect(struct peer *peer)
if (peer->password)
bgp_md5_set_connect(peer->fd, &peer->su, peer->password);
/* Bind socket. */
bgp_bind(peer);
/* Update source bind. */
if (bgp_update_source(peer) < 0) {
return connect_error;
@ -642,12 +628,12 @@ int bgp_getsockname(struct peer *peer)
return -1;
#endif
}
return 0;
}
static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen,
struct bgp *bgp)
{
struct bgp_listener *listener;
int ret, en;
@ -683,8 +669,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
return ret;
}
listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
listener->fd = sock;
/* this socket needs a change of ns. record bgp back pointer */
if (bgp->vrf_id != VRF_DEFAULT &&
vrf_is_mapped_on_netns(bgp->vrf_id))
listener->bgp = bgp;
memcpy(&listener->su, sa, salen);
listener->thread = NULL;
thread_add_read(bm->master, bgp_accept, listener, sock,
@ -695,7 +687,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
}
/* IPv6 supported version of BGP server socket setup. */
int bgp_socket(unsigned short port, const char *address)
int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)
{
struct addrinfo *ainfo;
struct addrinfo *ainfo_save;
@ -710,7 +702,12 @@ int bgp_socket(unsigned short port, const char *address)
snprintf(port_str, sizeof(port_str), "%d", port);
port_str[sizeof(port_str) - 1] = '\0';
ret = getaddrinfo(address, port_str, &req, &ainfo_save);
if (bgpd_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
ret = vrf_getaddrinfo(address, port_str, &req,
&ainfo_save, bgp->vrf_id);
if (bgpd_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
if (ret != 0) {
zlog_err("getaddrinfo: %s", gai_strerror(ret));
return -1;
@ -723,8 +720,13 @@ int bgp_socket(unsigned short port, const char *address)
if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
continue;
sock = socket(ainfo->ai_family, ainfo->ai_socktype,
ainfo->ai_protocol);
if (bgpd_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype,
ainfo->ai_protocol, bgp->vrf_id,
NULL);
if (bgpd_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
if (sock < 0) {
zlog_err("socket: %s", safe_strerror(errno));
continue;
@ -734,7 +736,8 @@ int bgp_socket(unsigned short port, const char *address)
* ttl=255 */
sockopt_ttl(ainfo->ai_family, sock, MAXTTL);
ret = bgp_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen);
ret = bgp_listener(sock, ainfo->ai_addr,
ainfo->ai_addrlen, bgp);
if (ret == 0)
++count;
else
@ -751,6 +754,32 @@ int bgp_socket(unsigned short port, const char *address)
return 0;
}
/* this function closes vrf socket
* this should be called only for vrf socket with netns backend
*/
void bgp_close_vrf_socket(struct bgp *bgp)
{
struct listnode *node, *next;
struct bgp_listener *listener;
if (!bgp)
return;
if (bm->listen_sockets == NULL)
return;
for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
if (listener->bgp == bgp) {
thread_cancel(listener->thread);
close(listener->fd);
listnode_delete(bm->listen_sockets, listener);
XFREE(MTYPE_BGP_LISTENER, listener);
}
}
}
/* this function closes main socket
*/
void bgp_close(void)
{
struct listnode *node, *next;
@ -760,6 +789,8 @@ void bgp_close(void)
return;
for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
if (listener->bgp)
continue;
thread_cancel(listener->thread);
close(listener->fd);
listnode_delete(bm->listen_sockets, listener);

View File

@ -23,7 +23,9 @@
#define BGP_SOCKET_SNDBUF_SIZE 65536
extern int bgp_socket(unsigned short, const char *);
extern int bgp_socket(struct bgp *bgp, unsigned short port,
const char *address);
extern void bgp_close_vrf_socket(struct bgp *bgp);
extern void bgp_close(void);
extern int bgp_connect(struct peer *);
extern int bgp_getsockname(struct peer *);

View File

@ -3076,6 +3076,19 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name,
}
}
}
/* for type5 command route-maps */
FOREACH_AFI_SAFI (afi, safi) {
if (bgp->adv_cmd_rmap[afi][safi].name &&
strcmp(rmap_name, bgp->adv_cmd_rmap[afi][safi].name) == 0) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug(
"Processing route_map %s update on advertise type5 route command",
rmap_name);
bgp_evpn_withdraw_type5_routes(bgp, afi, safi);
bgp_evpn_advertise_type5_routes(bgp, afi, safi);
}
}
}
static int bgp_route_map_process_update_cb(char *rmap_name)

View File

@ -3395,7 +3395,7 @@ DEFUN (no_neighbor_set_peer_group,
return CMD_WARNING_CONFIG_FAILED;
}
ret = peer_group_unbind(bgp, peer, group);
ret = peer_delete(peer);
return bgp_vty_return(vty, ret);
}
@ -7278,8 +7278,7 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,
safi = SAFI_MAX;
}
afi++;
if (!afi_wildcard
|| afi == AFI_L2VPN) /* special case, not handled yet */
if (!afi_wildcard)
afi = AFI_MAX;
}

View File

@ -1026,14 +1026,14 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
|| info->sub_type == BGP_ROUTE_AGGREGATE) {
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
}
if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|| bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
/* Metric is currently based on the best-path only */
metric = info->attr->med;
@ -1265,14 +1265,14 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi)
SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
if (peer->sort == BGP_PEER_IBGP) {
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
}
if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|| bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
if (bgp_debug_zebra(p)) {
char buf[PREFIX_STRLEN];
@ -1726,6 +1726,7 @@ static void bgp_zebra_connected(struct zclient *zclient)
static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
int filter = 0;
char buf[ETHER_ADDR_STRLEN];
vni_t l3vni = 0;
struct ethaddr rmac;
@ -1739,16 +1740,20 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
if (cmd == ZEBRA_L3VNI_ADD) {
stream_get(&rmac, s, sizeof(struct ethaddr));
originator_ip.s_addr = stream_get_ipv4(s);
stream_get(&filter, s, sizeof(int));
}
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s",
zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s filter %s",
(cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
vrf_id_to_name(vrf_id), l3vni,
prefix_mac2str(&rmac, buf, sizeof(buf)));
vrf_id_to_name(vrf_id),
l3vni,
prefix_mac2str(&rmac, buf, sizeof(buf)),
filter ? "prefix-routes-only" : "none");
if (cmd == ZEBRA_L3VNI_ADD)
bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip);
bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip,
filter);
else
bgp_evpn_local_l3vni_del(l3vni, vrf_id);

View File

@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp);
extern struct zclient *zclient;
/* handle main socket creation or deletion */
static int bgp_check_main_socket(bool create, struct bgp *bgp)
{
static int bgp_server_main_created;
struct listnode *bgpnode, *nbgpnode;
struct bgp *bgp_temp;
if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
return 0;
if (create == true) {
if (bgp_server_main_created)
return 0;
if (bgp_socket(bgp, bm->port, bm->address) < 0)
return BGP_ERR_INVALID_VALUE;
bgp_server_main_created = 1;
return 0;
}
if (!bgp_server_main_created)
return 0;
/* only delete socket on some cases */
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp_temp)) {
/* do not count with current bgp */
if (bgp_temp == bgp)
continue;
/* if other instance non VRF, do not delete socket */
if (bgp_temp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
return 0;
/* vrf lite, do not delete socket */
if (!vrf_is_mapped_on_netns(bgp_temp->vrf_id))
return 0;
}
bgp_close();
bgp_server_main_created = 0;
return 0;
}
void bgp_session_reset(struct peer *peer)
{
if (peer->doppelganger && (peer->doppelganger->status != Deleted)
@ -1200,7 +1236,6 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
peer_dst->config = peer_src->config;
peer_dst->local_as = peer_src->local_as;
peer_dst->ifindex = peer_src->ifindex;
peer_dst->port = peer_src->port;
(void)peer_sort(peer_dst);
peer_dst->rmap_type = peer_src->rmap_type;
@ -2981,11 +3016,67 @@ struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id)
return (vrf->info) ? (struct bgp *)vrf->info : NULL;
}
/* handle socket creation or deletion, if necessary
* this is called for all new BGP instances
*/
int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf,
vrf_id_t old_vrf_id, bool create)
{
int ret = 0;
/* Create BGP server socket, if listen mode not disabled */
if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
return 0;
if (bgp->name
&& bgp->inst_type == BGP_INSTANCE_TYPE_VRF
&& vrf) {
/*
* suppress vrf socket
*/
if (create == FALSE) {
if (vrf_is_mapped_on_netns(vrf->vrf_id))
bgp_close_vrf_socket(bgp);
else
ret = bgp_check_main_socket(create, bgp);
return ret;
}
/* do nothing
* if vrf_id did not change
*/
if (vrf->vrf_id == old_vrf_id)
return 0;
if (old_vrf_id != VRF_UNKNOWN) {
/* look for old socket. close it. */
bgp_close_vrf_socket(bgp);
}
/* if backend is not yet identified ( VRF_UNKNOWN) then
* creation will be done later
*/
if (vrf->vrf_id == VRF_UNKNOWN)
return 0;
/* if BGP VRF instance requested
* if backend is NETNS, create BGP server socket in the NETNS
*/
if (vrf_is_mapped_on_netns(bgp->vrf_id)) {
ret = bgp_socket(bgp, bm->port, bm->address);
if (ret < 0)
return BGP_ERR_INVALID_VALUE;
return 0;
}
}
/* if BGP VRF instance requested or VRF lite backend
* if BGP non VRF instance, create it
* if not already done
*/
return bgp_check_main_socket(create, bgp);
}
/* Called from VTY commands. */
int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
enum bgp_instance_type inst_type)
{
struct bgp *bgp;
struct vrf *vrf = NULL;
/* Multiple instance check. */
if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) {
@ -3033,25 +3124,19 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
bgp->t_rmap_def_originate_eval = NULL;
/* Create BGP server socket, if first instance. */
if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) {
if (bgp_socket(bm->port, bm->address) < 0)
return BGP_ERR_INVALID_VALUE;
}
listnode_add(bm->bgp, bgp);
/* If Default instance or VRF, link to the VRF structure, if present. */
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
|| bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
struct vrf *vrf;
vrf = bgp_vrf_lookup_by_instance_type(bgp);
if (vrf)
bgp_vrf_link(bgp, vrf);
}
/* BGP server socket already processed if BGP instance
* already part of the list
*/
bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true);
listnode_add(bm->bgp, bgp);
/* Register with Zebra, if needed */
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
bgp_zebra_instance_register(bgp);
@ -3188,8 +3273,6 @@ int bgp_delete(struct bgp *bgp)
* routes to be processed still referencing the struct bgp.
*/
listnode_delete(bm->bgp, bgp);
if (list_isempty(bm->bgp))
bgp_close();
/* Deregister from Zebra, if needed */
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
@ -3199,6 +3282,7 @@ int bgp_delete(struct bgp *bgp)
bgp_if_finish(bgp);
vrf = bgp_vrf_lookup_by_instance_type(bgp);
bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
if (vrf)
bgp_vrf_unlink(bgp, vrf);
@ -3337,11 +3421,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
struct listnode *bgpnode, *nbgpnode;
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
/* Skip VRFs, this function will not be invoked without
* an instance
/* Skip VRFs Lite only, this function will not be
* invoked without an instance
* when examining VRFs.
*/
if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) &&
!vrf_is_mapped_on_netns(bgp->vrf_id))
continue;
peer = hash_lookup(bgp->peerhash, &tmp_peer);
@ -7315,38 +7400,6 @@ int bgp_config_write(struct vty *vty)
if (bgp_option_check(BGP_OPT_CONFIG_CISCO))
vty_out(vty, " no auto-summary\n");
/* import route-target */
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
char *ecom_str;
struct listnode *node, *nnode;
struct ecommunity *ecom;
for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
ecom)) {
ecom_str = ecommunity_ecom2str(
ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
vty_out(vty, " route-target import %s\n",
ecom_str);
XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
}
}
/* export route-target */
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
char *ecom_str;
struct listnode *node, *nnode;
struct ecommunity *ecom;
for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
ecom)) {
ecom_str = ecommunity_ecom2str(
ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
vty_out(vty, " route-target export %s\n",
ecom_str);
XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
}
}
/* IPv4 unicast configuration. */
bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);

View File

@ -436,6 +436,7 @@ struct bgp {
#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
#define BGP_VRF_RD_CFGD (1 << 5)
#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6)
/* unique ID for auto derivation of RD for this vrf */
uint16_t vrf_rd_id;
@ -452,6 +453,9 @@ struct bgp {
/* list of corresponding l2vnis (struct bgpevpn) */
struct list *l2vnis;
/* route map for advertise ipv4/ipv6 unicast (type-5 routes) */
struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX];
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp)
@ -683,7 +687,6 @@ struct peer {
time_t readtime; /* Last read time */
time_t resettime; /* Last reset time */
ifindex_t ifindex; /* ifindex of the BGP connection. */
char *conf_if; /* neighbor interface config name. */
struct interface *ifp; /* corresponding interface */
char *ifname; /* bind interface name. */
@ -1351,6 +1354,9 @@ extern void bgp_instance_up(struct bgp *);
extern void bgp_instance_down(struct bgp *);
extern int bgp_delete(struct bgp *);
extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf,
vrf_id_t old_vrf_id, bool create);
extern int bgp_flag_set(struct bgp *, int);
extern int bgp_flag_unset(struct bgp *, int);
extern int bgp_flag_check(struct bgp *, int);

View File

@ -2977,6 +2977,11 @@ DEFUN_NOSH (vnc_vrf_policy,
struct rfapi_nve_group_cfg *rfg;
VTY_DECLVAR_CONTEXT(bgp, bgp);
if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
vty_out(vty, "Can't configure vrf-policy within a BGP VRF instance\n");
return CMD_WARNING_CONFIG_FAILED;
}
/* Search for name */
rfg = bgp_rfapi_cfg_match_byname(bgp, argv[1]->arg,
RFAPI_GROUP_CFG_VRF);
@ -3007,6 +3012,10 @@ DEFUN (vnc_no_vrf_policy,
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
/* silently return */
if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
return CMD_SUCCESS;
return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[2]->arg,
RFAPI_GROUP_CFG_VRF);
}

View File

@ -1,5 +1,6 @@
etc/frr/
usr/bin/vtysh
usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr etc/init.d/

View File

@ -1,5 +1,6 @@
etc/frr/
usr/bin/vtysh
usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr usr/lib/frr

View File

@ -11,6 +11,7 @@ man_MANS = $(MANPAGE_BUILDDIR)/frr.1
if PIMD
man_MANS += $(MANPAGE_BUILDDIR)/pimd.8
man_MANS += $(MANPAGE_BUILDDIR)/mtracebis.8
endif
if BGPD
@ -108,6 +109,7 @@ EXTRA_DIST = manpages/defines.rst \
manpages/vtysh.rst \
manpages/nhrpd.rst \
manpages/pimd.rst \
manpages/mtracebis.rst \
manpages/ripngd.rst \
manpages/frr.rst \
manpages/conf.py \

View File

@ -22,7 +22,7 @@ Add packages:
sudo yum install git autoconf automake libtool make gawk \
readline-devel texinfo net-snmp-devel groff pkgconfig \
json-c-devel pam-devel bison flex pytest c-ares-devel \
perl-XML-LibXML python-devel python-sphinx
perl-XML-LibXML python-devel systemd-devel python-sphinx
Get FRR, compile it and install it (from Git)
---------------------------------------------
@ -68,6 +68,7 @@ an example.)
--enable-group=frr \
--enable-vty-group=frrvt \
--enable-rtadv \
--enable-systemd=yes \
--disable-exampledir \
--enable-watchfrr \
--disable-ldpd \

View File

@ -318,6 +318,7 @@ man_pages = [
('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8),
('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
('mtracebis', 'mtracebis', "a multicast trace client", [], 8),
('ripd', 'ripd', fwfrr.format("a RIP "), [], 8),
('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8),
('zebra', 'zebra', 'a routing manager for use with associated FRRouting components.', [], 8),

View File

@ -1,3 +1,3 @@
.. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path]
.. |synopsis-options-hv| replace:: [-h] [-v]
.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), ldpd(8), eigrpd(8)
.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), ldpd(8), eigrpd(8), mtracebis(8)

View File

@ -8,7 +8,7 @@ The daemon may log to standard output, to a VTY, to a log file, or through syslo
SEE ALSO
========
zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), ldpd(8), eigrpd(8)
|seealso-programs|
|PACKAGE_URL|
BUGS

View File

@ -2,6 +2,8 @@
FRR
***
.. include:: defines.rst
SYNOPSIS
========
frr [ start ]

View File

@ -18,6 +18,7 @@ Welcome to FRR's documentation!
ospfclient
ospfd
pimd
mtracebis
ripd
ripngd
watchfrr

View File

@ -0,0 +1,39 @@
*********
MTRACEBIS
*********
.. include:: defines.rst
.. |PROGRAM| replace:: mtracebis
SYNOPSIS
========
|PROGRAM| |synopsis-options-hv|
|PROGRAM| <multicast source>
DESCRIPTION
===========
|PROGRAM| is a program to initiate multicast traceroute, or "mtrace", queries.
The initial version of the program requires multicast source IP address and
initiates a weak traceroute across the network. This tests whether the
interfaces towards the source are multicast enabled. The first query sent is a
full query, capable of crossing the network all the way to the source. If this
fails, hop-by-hop queries are initiated.
Hop-by-hop queries start by requesting only a response from the nearest router.
Following that, next query is extended to the next two routers, and so on...
until a set of routers is tested for connectivity.
FILES
=====
|INSTALL_PREFIX_SBIN|/|PROGRAM|
The default location of the |PROGRAM| binary.
.. include:: epilogue.rst
AUTHORS
=======
Mladen Sablic

View File

@ -2,6 +2,8 @@
OSPFCLIENT
**********
.. include:: defines.rst
SYNOPSIS
========
ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>

View File

@ -37,6 +37,11 @@ OPTIONS available for the |DAEMON| command:
Note that this affects Linux only.
.. option:: -n, --vrfwnetns
Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite support from the Linux kernel. This option permits discovering Linux named network namespaces and mapping it to FRR VRF contexts.
FILES
=====

View File

@ -94,13 +94,14 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient,
return 0;
}
static int eigrp_zebra_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct prefix p;
enum zapi_route_notify_owner note;
uint32_t table;
if (!zapi_route_notify_decode(zclient->ibuf, &p, &note))
if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note))
return -1;
return 0;
@ -128,7 +129,7 @@ void eigrp_zebra_init(void)
zclient->interface_address_delete = eigrp_interface_address_delete;
zclient->redistribute_route_add = eigrp_zebra_read_route;
zclient->redistribute_route_del = eigrp_zebra_read_route;
zclient->notify_owner = eigrp_zebra_notify_owner;
zclient->route_notify_owner = eigrp_zebra_route_notify_owner;
}

View File

@ -0,0 +1,23 @@
/* Copyright (c) 2015 6WIND S.A.
* Author: Nicolas Dichtel <nicolas.dichtel@6wind.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#ifndef _LINUX_NET_NAMESPACE_H_
#define _LINUX_NET_NAMESPACE_H_
/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */
enum {
NETNSA_NONE,
#define NETNSA_NSID_NOT_ASSIGNED -1
NETNSA_NSID,
NETNSA_PID,
NETNSA_FD,
__NETNSA_MAX,
};
#define NETNSA_MAX (__NETNSA_MAX - 1)
#endif /* _LINUX_NET_NAMESPACE_H_ */

View File

@ -6,4 +6,5 @@ noinst_HEADERS += \
include/linux/neighbour.h \
include/linux/rtnetlink.h \
include/linux/socket.h \
include/linux/net_namespace.h \
# end

View File

@ -1168,7 +1168,6 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
struct isis_area *area = circuit->area;
bool change = circuit->ip_router != ip_router
|| circuit->ipv6_router != ipv6_router;
bool was_enabled = !!circuit->area;
area->ip_circuits += ip_router - circuit->ip_router;
area->ipv6_circuits += ipv6_router - circuit->ipv6_router;
@ -1182,8 +1181,6 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
if (!ip_router && !ipv6_router)
isis_csm_state_change(ISIS_DISABLE, circuit, area);
else if (!was_enabled)
isis_csm_state_change(ISIS_ENABLE, circuit, area);
else
lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
}

View File

@ -306,8 +306,11 @@ if_reset(struct iface *iface, int af)
ia = iface_af_get(iface, af);
if_stop_hello_timer(ia);
while ((adj = RB_ROOT(ia_adj_head, &ia->adj_tree)) != NULL)
while (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) {
adj = RB_ROOT(ia_adj_head, &ia->adj_tree);
adj_del(adj, S_SHUTDOWN);
}
/* try to cleanup */
switch (af) {

View File

@ -76,16 +76,21 @@ l2vpn_del(struct l2vpn *l2vpn)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
while ((pw = RB_ROOT(l2vpn_pw_head,
&l2vpn->pw_inactive_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);
}

View File

@ -1324,8 +1324,11 @@ lde_nbr_clear(void)
{
struct lde_nbr *ln;
while ((ln = RB_ROOT(nbr_tree, &lde_nbrs)) != NULL)
while (!RB_EMPTY(nbr_tree, &lde_nbrs)) {
ln = RB_ROOT(nbr_tree, &lde_nbrs);
lde_nbr_del(ln);
}
}
static void

View File

@ -129,7 +129,9 @@ fec_clear(struct fec_tree *fh, void (*free_cb)(void *))
{
struct fec *f;
while ((f = RB_ROOT(fec_tree, fh)) != NULL) {
while (!RB_EMPTY(fec_tree, fh)) {
f = RB_ROOT(fec_tree, fh);
fec_remove(fh, f);
free_cb(f);
}

View File

@ -1475,18 +1475,23 @@ l2vpn_del_api(struct ldpd_conf *conf, struct l2vpn *l2vpn)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
QOBJ_UNREG(lif);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
while ((pw = RB_ROOT(l2vpn_pw_head,
&l2vpn->pw_inactive_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);

View File

@ -1066,13 +1066,17 @@ ldp_config_reset_main(struct ldpd_conf *conf)
struct iface *iface;
struct nbr_params *nbrp;
while ((iface = RB_ROOT(iface_head, &conf->iface_tree)) != NULL) {
while (!RB_EMPTY(iface_head, &conf->iface_tree)) {
iface = RB_ROOT(iface_head, &conf->iface_tree);
QOBJ_UNREG(iface);
RB_REMOVE(iface_head, &conf->iface_tree, iface);
free(iface);
}
while ((nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree)) != NULL) {
while (!RB_EMPTY(nbrp_head, &conf->nbrp_tree)) {
nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree);
QOBJ_UNREG(nbrp);
RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
free(nbrp);
@ -1128,20 +1132,25 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
while ((l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree)) != NULL) {
while ((lif = RB_ROOT(l2vpn_if_head,
&l2vpn->if_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_head, &conf->l2vpn_tree)) {
l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree);
while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
QOBJ_UNREG(lif);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
while ((pw = RB_ROOT(l2vpn_pw_head,
&l2vpn->pw_inactive_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);
@ -1160,19 +1169,27 @@ ldp_clear_config(struct ldpd_conf *xconf)
struct nbr_params *nbrp;
struct l2vpn *l2vpn;
while ((iface = RB_ROOT(iface_head, &xconf->iface_tree)) != NULL) {
while (!RB_EMPTY(iface_head, &xconf->iface_tree)) {
iface = RB_ROOT(iface_head, &xconf->iface_tree);
RB_REMOVE(iface_head, &xconf->iface_tree, iface);
free(iface);
}
while ((tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree)) != NULL) {
while (!RB_EMPTY(tnbr_head, &xconf->tnbr_tree)) {
tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree);
RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr);
free(tnbr);
}
while ((nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree)) != NULL) {
while (!RB_EMPTY(nbrp_head, &xconf->nbrp_tree)) {
nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree);
RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp);
free(nbrp);
}
while ((l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree)) != NULL) {
while (!RB_EMPTY(l2vpn_head, &xconf->l2vpn_tree)) {
l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree);
RB_REMOVE(l2vpn_head, &xconf->l2vpn_tree, l2vpn);
l2vpn_del(l2vpn);
}

View File

@ -219,8 +219,11 @@ ldpe_shutdown(void)
assert(if_addr != LIST_FIRST(&global.addr_list));
free(if_addr);
}
while ((adj = RB_ROOT(global_adj_head, &global.adj_tree)) != NULL)
while (!RB_EMPTY(global_adj_head, &global.adj_tree)) {
adj = RB_ROOT(global_adj_head, &global.adj_tree);
adj_del(adj, S_SHUTDOWN);
}
/* clean up */
if (iev_lde)

View File

@ -316,7 +316,9 @@ nbr_del(struct nbr *nbr)
mapping_list_clr(&nbr->release_list);
mapping_list_clr(&nbr->abortreq_list);
while ((adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree)) != NULL) {
while (!RB_EMPTY(nbr_adj_head, &nbr->adj_tree)) {
adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree);
adj->nbr = NULL;
RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
}

View File

@ -62,7 +62,7 @@ const char *node_names[] = {
"aaa", // AAA_NODE,
"keychain", // KEYCHAIN_NODE,
"keychain key", // KEYCHAIN_KEY_NODE,
"logical-router", // NS_NODE,
"logical-router", // LOGICALROUTER_NODE,
"vrf", // VRF_NODE,
"interface", // INTERFACE_NODE,
"zebra", // ZEBRA_NODE,
@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty)
break;
case INTERFACE_NODE:
case PW_NODE:
case NS_NODE:
case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case BGP_NODE:
@ -1376,7 +1376,7 @@ DEFUN (config_end,
case CONFIG_NODE:
case INTERFACE_NODE:
case PW_NODE:
case NS_NODE:
case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case RIP_NODE:

View File

@ -85,7 +85,7 @@ enum node_type {
AAA_NODE, /* AAA node. */
KEYCHAIN_NODE, /* Key-chain node. */
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
NS_NODE, /* Logical-Router node. */
LOGICALROUTER_NODE, /* Logical-Router node. */
VRF_NODE, /* VRF mode node. */
INTERFACE_NODE, /* Interface mode node. */
ZEBRA_NODE, /* zebra connection node. */

View File

@ -384,29 +384,35 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty)
{
struct interface *ifp;
ifp = if_lookup_by_name(name, vrf_id);
if (ifp)
return ifp;
/* Not Found on same VRF. If the interface command
* was entered in vty without a VRF (passed as VRF_DEFAULT),
* accept the ifp we found. If a vrf was entered and there is
* a mismatch, reject it if from vty.
*/
ifp = if_lookup_by_name_all_vrf(name);
if (ifp) {
if (ifp->vrf_id == vrf_id)
if (!ifp)
return if_create(name, vrf_id);
if (vty) {
if (vrf_id == VRF_DEFAULT)
return ifp;
/* Found a match on a different VRF. If the interface command
* was entered in vty without a VRF (passed as VRF_DEFAULT),
* accept the ifp we found. If a vrf was entered and there is
* a mismatch, reject it if from vty. If it came from the kernel
* or by way of zclient, believe it and update the ifp
* accordingly.
*/
if (vty) {
if (vrf_id == VRF_DEFAULT)
return ifp;
return NULL;
} else {
if_update_to_new_vrf(ifp, vrf_id);
return ifp;
}
return NULL;
}
return if_create(name, vrf_id);
/* if vrf backend uses NETNS, then
* this should not be considered as an update
* then create the new interface
*/
if (ifp->vrf_id != vrf_id &&
vrf_is_mapped_on_netns(vrf_id))
return if_create(name, vrf_id);
/* If it came from the kernel
* or by way of zclient, believe it and update
* the ifp accordingly.
*/
if_update_to_new_vrf(ifp, vrf_id);
return ifp;
}
void if_set_index(struct interface *ifp, ifindex_t ifindex)
@ -1064,7 +1070,7 @@ ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex)
rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
if (! rn)
return NULL;
ifp = rn->info;
route_unlock_node (rn);
return ifp;
@ -1078,7 +1084,9 @@ void if_terminate(struct vrf *vrf)
{
struct interface *ifp;
while ((ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name)) != NULL) {
while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name);
if (ifp->node) {
ifp->node->info = NULL;
route_unlock_node(ifp->node);

159
lib/logicalrouter.c Normal file
View File

@ -0,0 +1,159 @@
/*
* Logical Router functions.
* Copyright (C) 2018 6WIND S.A.
*
* 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 "ns.h"
#include "log.h"
#include "memory.h"
#include "command.h"
#include "vty.h"
#include "logicalrouter.h"
/* Comment that useless define to avoid compilation error
* in order to use it, one could provide the kind of NETNS to NS backend
* so that the allocation will match the logical router
* DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER, "LogicalRouter Context")
*/
DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER_NAME, "Logical Router Name")
/* Logical Router node has no interface. */
static struct cmd_node logicalrouter_node = {LOGICALROUTER_NODE, "",
1};
static int logicalrouter_backend;
/* Get a NS. If not found, create one. */
static struct ns *logicalrouter_get(ns_id_t ns_id)
{
struct ns *ns;
ns = ns_lookup(ns_id);
if (ns)
return (ns);
ns = ns_get_created(ns, NULL, ns_id);
return ns;
}
static int logicalrouter_is_backend_netns(void)
{
return (logicalrouter_backend == LOGICALROUTER_BACKEND_NETNS);
}
DEFUN_NOSH (logicalrouter,
logicalrouter_cmd,
"logical-router (1-65535) ns NAME",
"Enable a logical-router\n"
"Specify the logical-router indentifier\n"
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
int idx_number = 1;
int idx_name = 3;
ns_id_t ns_id;
struct ns *ns = NULL;
char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
if (!pathname)
return CMD_WARNING_CONFIG_FAILED;
ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
ns = logicalrouter_get(ns_id);
if (ns->name && strcmp(ns->name, pathname) != 0) {
vty_out(vty, "NS %u is already configured with NETNS %s\n",
ns->ns_id, ns->name);
return CMD_WARNING;
}
if (!ns->name)
ns->name = XSTRDUP(MTYPE_LOGICALROUTER_NAME, pathname);
if (!ns_enable(ns, NULL)) {
vty_out(vty, "Can not associate NS %u with NETNS %s\n",
ns->ns_id, ns->name);
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
DEFUN (no_logicalrouter,
no_logicalrouter_cmd,
"no logical-router (1-65535) ns NAME",
NO_STR
"Enable a Logical-Router\n"
"Specify the Logical-Router identifier\n"
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
int idx_number = 2;
int idx_name = 4;
ns_id_t ns_id;
struct ns *ns = NULL;
char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
if (!pathname)
return CMD_WARNING_CONFIG_FAILED;
ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
ns = ns_lookup(ns_id);
if (!ns) {
vty_out(vty, "NS %u is not found\n", ns_id);
return CMD_SUCCESS;
}
if (ns->name && strcmp(ns->name, pathname) != 0) {
vty_out(vty, "Incorrect NETNS file name\n");
return CMD_WARNING_CONFIG_FAILED;
}
ns_disable(ns);
if (ns->name) {
XFREE(MTYPE_LOGICALROUTER_NAME, ns->name);
ns->name = NULL;
}
return CMD_SUCCESS;
}
/* Initialize NS module. */
void logicalrouter_init(int (*writefunc)(struct vty *vty))
{
if (ns_have_netns() && logicalrouter_is_backend_netns()) {
/* Install LogicalRouter commands. */
install_node(&logicalrouter_node, writefunc);
install_element(CONFIG_NODE, &logicalrouter_cmd);
install_element(CONFIG_NODE, &no_logicalrouter_cmd);
}
}
void logicalrouter_terminate(void)
{
ns_terminate();
}
void logicalrouter_configure_backend(int backend_netns)
{
logicalrouter_backend = backend_netns;
}

41
lib/logicalrouter.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Logical Router related header.
* Copyright (C) 2018 6WIND S.A.
*
* 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 _ZEBRA_LOGICAL_ROUTER_H
#define _ZEBRA_LOGICAL_ROUTER_H
/* Logical Router Backend defines */
#define LOGICALROUTER_BACKEND_OFF 0
#define LOGICALROUTER_BACKEND_NETNS 1
/*
* Logical Router initializer/destructor
*/
extern void logicalrouter_init(int (*writefunc)(struct vty *vty));
extern void logicalrouter_terminate(void);
/* used to configure backend for logical router
* Currently, the whole NETNS feature is exclusively shared
* between logical router and VRF backend NETNS
* However, when logical router feature will be available,
* one can think of having exclusivity only per NETNS
*/
extern void logicalrouter_configure_backend(int backend_netns);
#endif /*_ZEBRA_LOGICAL_ROUTER_H*/

539
lib/netns_linux.c Normal file
View File

@ -0,0 +1,539 @@
/*
* NS functions.
* Copyright (C) 2014 6WIND S.A.
*
* This file is part of GNU Zebra.
*
* GNU Zebra 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.
*
* GNU Zebra 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>
#ifdef HAVE_NETNS
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#endif
/* for basename */
#include <libgen.h>
#include "if.h"
#include "ns.h"
#include "log.h"
#include "memory.h"
#include "command.h"
#include "vty.h"
#include "vrf.h"
DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context")
DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name")
static inline int ns_compare(const struct ns *ns, const struct ns *ns2);
static struct ns *ns_lookup_name_internal(const char *name);
RB_GENERATE(ns_head, ns, entry, ns_compare)
struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
static struct ns *default_ns;
static int ns_current_ns_fd;
static int ns_default_ns_fd;
static int ns_debug;
#ifndef CLONE_NEWNET
#define CLONE_NEWNET 0x40000000
/* New network namespace (lo, device, names sockets, etc) */
#endif
#ifndef HAVE_SETNS
static inline int setns(int fd, int nstype)
{
#ifdef __NR_setns
return syscall(__NR_setns, fd, nstype);
#else
errno = EINVAL;
return -1;
#endif
}
#endif /* !HAVE_SETNS */
#ifdef HAVE_NETNS
static int have_netns_enabled = -1;
#endif /* HAVE_NETNS */
/* default NS ID value used when VRF backend is not NETNS */
#define NS_DEFAULT_INTERNAL 0
static int have_netns(void)
{
#ifdef HAVE_NETNS
if (have_netns_enabled < 0) {
int fd = open(NS_DEFAULT_NAME, O_RDONLY);
if (fd < 0)
have_netns_enabled = 0;
else {
have_netns_enabled = 1;
close(fd);
}
}
return have_netns_enabled;
#else
return 0;
#endif
}
/* Holding NS hooks */
struct ns_master {
int (*ns_new_hook)(struct ns *ns);
int (*ns_delete_hook)(struct ns *ns);
int (*ns_enable_hook)(struct ns *ns);
int (*ns_disable_hook)(struct ns *ns);
} ns_master = {
0,
};
static int ns_is_enabled(struct ns *ns);
static inline int ns_compare(const struct ns *a, const struct ns *b)
{
return (a->ns_id - b->ns_id);
}
/* Look up a NS by identifier. */
static struct ns *ns_lookup_internal(ns_id_t ns_id)
{
struct ns ns;
ns.ns_id = ns_id;
return RB_FIND(ns_head, &ns_tree, &ns);
}
/* Look up a NS by name */
static struct ns *ns_lookup_name_internal(const char *name)
{
struct ns *ns = NULL;
RB_FOREACH (ns, ns_head, &ns_tree) {
if (ns->name != NULL) {
if (strcmp(name, ns->name) == 0)
return ns;
}
}
return NULL;
}
static struct ns *ns_get_created_internal(struct ns *ns, char *name,
ns_id_t ns_id)
{
int created = 0;
/*
* Initialize interfaces.
*/
if (!ns && !name && ns_id != NS_UNKNOWN)
ns = ns_lookup_internal(ns_id);
if (!ns && name)
ns = ns_lookup_name_internal(name);
if (!ns) {
ns = XCALLOC(MTYPE_NS, sizeof(struct ns));
ns->ns_id = ns_id;
if (name)
ns->name = XSTRDUP(MTYPE_NS_NAME, name);
ns->fd = -1;
RB_INSERT(ns_head, &ns_tree, ns);
created = 1;
}
if (ns_id != ns->ns_id) {
RB_REMOVE(ns_head, &ns_tree, ns);
ns->ns_id = ns_id;
RB_INSERT(ns_head, &ns_tree, ns);
}
if (!created)
return ns;
if (ns_debug) {
if (ns->ns_id != NS_UNKNOWN)
zlog_info("NS %u is created.", ns->ns_id);
else
zlog_info("NS %s is created.", ns->name);
}
if (ns_master.ns_new_hook)
(*ns_master.ns_new_hook) (ns);
return ns;
}
/*
* Enable a NS - that is, let the NS be ready to use.
* The NS_ENABLE_HOOK callback will be called to inform
* that they can allocate resources in this NS.
*
* RETURN: 1 - enabled successfully; otherwise, 0.
*/
static int ns_enable_internal(struct ns *ns, void (*func)(ns_id_t, void *))
{
if (!ns_is_enabled(ns)) {
if (have_netns()) {
ns->fd = open(ns->name, O_RDONLY);
} else {
ns->fd = -2;
/* Remember ns_enable_hook has been called */
errno = -ENOTSUP;
}
if (!ns_is_enabled(ns)) {
zlog_err("Can not enable NS %u: %s!", ns->ns_id,
safe_strerror(errno));
return 0;
}
/* Non default NS. leave */
if (ns->ns_id == NS_UNKNOWN) {
zlog_err("Can not enable NS %s %u: Invalid NSID",
ns->name, ns->ns_id);
return 0;
}
if (func)
func(ns->ns_id, (void *)ns->vrf_ctxt);
if (ns_debug) {
if (have_netns())
zlog_info("NS %u is associated with NETNS %s.",
ns->ns_id, ns->name);
zlog_info("NS %u is enabled.", ns->ns_id);
}
/* zebra first receives NS enable event,
* then VRF enable event
*/
if (ns_master.ns_enable_hook)
(*ns_master.ns_enable_hook)(ns);
}
return 1;
}
/*
* Check whether the NS is enabled - that is, whether the NS
* is ready to allocate resources. Currently there's only one
* type of resource: socket.
*/
static int ns_is_enabled(struct ns *ns)
{
if (have_netns())
return ns && ns->fd >= 0;
else
return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT;
}
/*
* Disable a NS - that is, let the NS be unusable.
* The NS_DELETE_HOOK callback will be called to inform
* that they must release the resources in the NS.
*/
static void ns_disable_internal(struct ns *ns)
{
if (ns_is_enabled(ns)) {
if (ns_debug)
zlog_info("NS %u is to be disabled.",
ns->ns_id);
if (ns_master.ns_disable_hook)
(*ns_master.ns_disable_hook)(ns);
if (have_netns())
close(ns->fd);
ns->fd = -1;
}
}
struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
{
return ns_get_created_internal(ns, name, ns_id);
}
int ns_have_netns(void)
{
return have_netns();
}
/* Delete a NS. This is called in ns_terminate(). */
void ns_delete(struct ns *ns)
{
if (ns_debug)
zlog_info("NS %u is to be deleted.", ns->ns_id);
ns_disable(ns);
if (ns_master.ns_delete_hook)
(*ns_master.ns_delete_hook)(ns);
/*
* I'm not entirely sure if the vrf->iflist
* needs to be moved into here or not.
*/
// if_terminate (&ns->iflist);
RB_REMOVE(ns_head, &ns_tree, ns);
if (ns->name)
XFREE(MTYPE_NS_NAME, ns->name);
XFREE(MTYPE_NS, ns);
}
/* Look up the data pointer of the specified VRF. */
void *
ns_info_lookup(ns_id_t ns_id)
{
struct ns *ns = ns_lookup_internal(ns_id);
return ns ? ns->info : NULL;
}
/* Look up a NS by name */
struct ns *ns_lookup_name(const char *name)
{
return ns_lookup_name_internal(name);
}
int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *))
{
return ns_enable_internal(ns, func);
}
void ns_disable(struct ns *ns)
{
return ns_disable_internal(ns);
}
struct ns *ns_lookup(ns_id_t ns_id)
{
return ns_lookup_internal(ns_id);
}
void ns_walk_func(int (*func)(struct ns *))
{
struct ns *ns = NULL;
RB_FOREACH (ns, ns_head, &ns_tree)
func(ns);
}
const char *ns_get_name(struct ns *ns)
{
if (!ns)
return NULL;
return ns->name;
}
/* Add a NS hook. Please add hooks before calling ns_init(). */
void ns_add_hook(int type, int (*func)(struct ns *))
{
switch (type) {
case NS_NEW_HOOK:
ns_master.ns_new_hook = func;
break;
case NS_DELETE_HOOK:
ns_master.ns_delete_hook = func;
break;
case NS_ENABLE_HOOK:
ns_master.ns_enable_hook = func;
break;
case NS_DISABLE_HOOK:
ns_master.ns_disable_hook = func;
break;
default:
break;
}
}
/*
* NS realization with NETNS
*/
char *ns_netns_pathname(struct vty *vty, const char *name)
{
static char pathname[PATH_MAX];
char *result;
char *check_base;
if (name[0] == '/') /* absolute pathname */
result = realpath(name, pathname);
else {
/* relevant pathname */
char tmp_name[PATH_MAX];
snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name);
result = realpath(tmp_name, pathname);
}
if (!result) {
if (vty)
vty_out(vty, "Invalid pathname: %s\n",
safe_strerror(errno));
else
zlog_warn("Invalid pathname: %s",
safe_strerror(errno));
return NULL;
}
check_base = basename(pathname);
if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) {
if (vty)
vty_out(vty, "NS name (%s) invalid: too long (>%d)\n",
check_base, NS_NAMSIZ-1);
else
zlog_warn("NS name (%s) invalid: too long (>%d)",
check_base, NS_NAMSIZ-1);
return NULL;
}
return pathname;
}
void ns_init(void)
{
static int ns_initialised;
ns_debug = 0;
/* silently return as initialisation done */
if (ns_initialised == 1)
return;
errno = 0;
#ifdef HAVE_NETNS
if (have_netns_enabled < 0)
ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY);
else
ns_default_ns_fd = -1;
#else
ns_default_ns_fd = -1;
default_ns = NULL;
#endif /* HAVE_NETNS */
if (ns_default_ns_fd == -1)
zlog_err("NS initialisation failure (%s)",
safe_strerror(errno));
ns_current_ns_fd = -1;
ns_initialised = 1;
}
/* Initialize NS module. */
void ns_init_management(ns_id_t default_ns_id)
{
int fd;
ns_init();
default_ns = ns_get_created_internal(NULL, NULL, default_ns_id);
if (!default_ns) {
zlog_err("%s: failed to create the default NS!",
__func__);
exit(1);
}
if (have_netns()) {
fd = open(NS_DEFAULT_NAME, O_RDONLY);
default_ns->fd = fd;
}
/* Set the default NS name. */
default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
if (ns_debug)
zlog_info("%s: default NSID is %u",
__func__, default_ns->ns_id);
/* Enable the default NS. */
if (!ns_enable(default_ns, NULL)) {
zlog_err("%s: failed to enable the default NS!",
__func__);
exit(1);
}
}
/* Terminate NS module. */
void ns_terminate(void)
{
struct ns *ns;
while (!RB_EMPTY(ns_head, &ns_tree)) {
ns = RB_ROOT(ns_head, &ns_tree);
ns_delete(ns);
}
}
int ns_switch_to_netns(const char *name)
{
int ret;
int fd;
if (name == NULL)
return -1;
if (ns_default_ns_fd == -1)
return -1;
fd = open(name, O_RDONLY);
if (fd == -1) {
errno = EINVAL;
return -1;
}
ret = setns(fd, CLONE_NEWNET);
ns_current_ns_fd = fd;
close(fd);
return ret;
}
/* returns 1 if switch() was not called before
* return status of setns() otherwise
*/
int ns_switchback_to_initial(void)
{
if (ns_current_ns_fd != -1 && ns_default_ns_fd != -1) {
int ret;
ret = setns(ns_default_ns_fd, CLONE_NEWNET);
ns_current_ns_fd = -1;
return ret;
}
/* silently ignore if setns() is not called */
return 1;
}
/* Create a socket for the NS. */
int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
{
struct ns *ns = ns_lookup(ns_id);
int ret;
if (!ns || !ns_is_enabled(ns)) {
errno = EINVAL;
return -1;
}
if (have_netns()) {
ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0;
if (ret >= 0) {
ret = socket(domain, type, protocol);
if (ns_id != NS_DEFAULT) {
setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET);
ns_current_ns_fd = ns_id;
}
}
} else
ret = socket(domain, type, protocol);
return ret;
}
ns_id_t ns_get_default_id(void)
{
if (default_ns)
return default_ns->ns_id;
return NS_UNKNOWN;
}

165
lib/netns_other.c Normal file
View File

@ -0,0 +1,165 @@
/*
* NetNS backend for non Linux systems
* Copyright (C) 2018 6WIND S.A.
*
* 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
*/
#if !defined(GNU_LINUX) && (defined(SUNOS_5) || defined(OPEN_BSD))
/* SUNOS_5 or OPEN_BSD */
#include <zebra.h>
#include "ns.h"
#include "log.h"
#include "memory.h"
DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context")
DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name")
static inline int ns_compare(const struct ns *ns, const struct ns *ns2);
RB_GENERATE(ns_head, ns, entry, ns_compare)
struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
static inline int ns_compare(const struct ns *a, const struct ns *b)
{
return (a->ns_id - b->ns_id);
}
void ns_terminate(void)
{
}
/* API to initialize NETNS managerment
* parameter is the default ns_id
*/
void ns_init_management(ns_id_t ns_id)
{
}
/*
* NS utilities
*/
/* Create a socket serving for the given NS
*/
int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
{
return -1;
}
/* return the path of the NETNS */
char *ns_netns_pathname(struct vty *vty, const char *name)
{
return NULL;
}
/* Parse and execute a function on all the NETNS */
void ns_walk_func(int (*func)(struct ns *))
{
}
/* API to get the NETNS name, from the ns pointer */
const char *ns_get_name(struct ns *ns)
{
return NULL;
}
/* only called from vrf ( when removing netns from vrf)
* or at VRF or logical router termination
*/
void ns_delete(struct ns *ns)
{
}
/* return > 0 if netns is available
* called by VRF to check netns backend is available for VRF
*/
int ns_have_netns(void)
{
return 0;
}
/* API to get context information of a NS */
void *ns_info_lookup(ns_id_t ns_id)
{
return NULL;
}
/*
* NS init routine
* should be called from backendx
*/
void ns_init(void)
{
}
/* API to retrieve default NS */
ns_id_t ns_get_default_id(void)
{
return NS_UNKNOWN;
}
/* API that can be used to change from NS */
int ns_switchback_to_initial(void)
{
return 0;
}
int ns_switch_to_netns(const char *netns_name)
{
return 0;
}
/*
* NS handling routines.
* called by modules that use NS backend
*/
/* API to search for already present NETNS */
struct ns *ns_lookup(ns_id_t ns_id)
{
return NULL;
}
struct ns *ns_lookup_name(const char *name)
{
return NULL;
}
/* API to handle NS : creation, enable, disable
* for enable, a callback function is passed as parameter
* the callback belongs to the module that uses NS as backend
* upon enabling the NETNS, the upper layer is informed
*/
int ns_enable(struct ns *ns, int (*func)(ns_id_t, void *))
{
return 0;
}
struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
{
return NULL;
}
void ns_disable(struct ns *ns)
{
}
#endif /* !GNU_LINUX */

453
lib/ns.c
View File

@ -1,453 +0,0 @@
/*
* NS functions.
* Copyright (C) 2014 6WIND S.A.
*
* This file is part of GNU Zebra.
*
* GNU Zebra 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.
*
* GNU Zebra 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>
#ifdef HAVE_NETNS
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#endif
#include "if.h"
#include "ns.h"
#include "log.h"
#include "memory.h"
#include "command.h"
#include "vty.h"
DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router")
DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name")
static __inline int ns_compare(const struct ns *, const struct ns *);
static struct ns *ns_lookup(ns_id_t);
RB_GENERATE(ns_head, ns, entry, ns_compare)
struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
#ifndef CLONE_NEWNET
#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */
#endif
#ifndef HAVE_SETNS
static inline int setns(int fd, int nstype)
{
#ifdef __NR_setns
return syscall(__NR_setns, fd, nstype);
#else
errno = ENOSYS;
return -1;
#endif
}
#endif /* HAVE_SETNS */
#ifdef HAVE_NETNS
#define NS_DEFAULT_NAME "/proc/self/ns/net"
static int have_netns_enabled = -1;
#else /* !HAVE_NETNS */
#define NS_DEFAULT_NAME "Default-logical-router"
#endif /* HAVE_NETNS */
static int have_netns(void)
{
#ifdef HAVE_NETNS
if (have_netns_enabled < 0) {
int fd = open(NS_DEFAULT_NAME, O_RDONLY);
if (fd < 0)
have_netns_enabled = 0;
else {
have_netns_enabled = 1;
close(fd);
}
}
return have_netns_enabled;
#else
return 0;
#endif
}
/* Holding NS hooks */
struct ns_master {
int (*ns_new_hook)(ns_id_t, void **);
int (*ns_delete_hook)(ns_id_t, void **);
int (*ns_enable_hook)(ns_id_t, void **);
int (*ns_disable_hook)(ns_id_t, void **);
} ns_master = {
0,
};
static int ns_is_enabled(struct ns *ns);
static int ns_enable(struct ns *ns);
static void ns_disable(struct ns *ns);
static __inline int ns_compare(const struct ns *a, const struct ns *b)
{
return (a->ns_id - b->ns_id);
}
/* Get a NS. If not found, create one. */
static struct ns *ns_get(ns_id_t ns_id)
{
struct ns *ns;
ns = ns_lookup(ns_id);
if (ns)
return (ns);
ns = XCALLOC(MTYPE_NS, sizeof(struct ns));
ns->ns_id = ns_id;
ns->fd = -1;
RB_INSERT(ns_head, &ns_tree, ns);
/*
* Initialize interfaces.
*
* I'm not sure if this belongs here or in
* the vrf code.
*/
// if_init (&ns->iflist);
zlog_info("NS %u is created.", ns_id);
if (ns_master.ns_new_hook)
(*ns_master.ns_new_hook)(ns_id, &ns->info);
return ns;
}
/* Delete a NS. This is called in ns_terminate(). */
static void ns_delete(struct ns *ns)
{
zlog_info("NS %u is to be deleted.", ns->ns_id);
ns_disable(ns);
if (ns_master.ns_delete_hook)
(*ns_master.ns_delete_hook)(ns->ns_id, &ns->info);
/*
* I'm not entirely sure if the vrf->iflist
* needs to be moved into here or not.
*/
// if_terminate (&ns->iflist);
RB_REMOVE(ns_head, &ns_tree, ns);
if (ns->name)
XFREE(MTYPE_NS_NAME, ns->name);
XFREE(MTYPE_NS, ns);
}
/* Look up a NS by identifier. */
static struct ns *ns_lookup(ns_id_t ns_id)
{
struct ns ns;
ns.ns_id = ns_id;
return (RB_FIND(ns_head, &ns_tree, &ns));
}
/*
* Check whether the NS is enabled - that is, whether the NS
* is ready to allocate resources. Currently there's only one
* type of resource: socket.
*/
static int ns_is_enabled(struct ns *ns)
{
if (have_netns())
return ns && ns->fd >= 0;
else
return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT;
}
/*
* Enable a NS - that is, let the NS be ready to use.
* The NS_ENABLE_HOOK callback will be called to inform
* that they can allocate resources in this NS.
*
* RETURN: 1 - enabled successfully; otherwise, 0.
*/
static int ns_enable(struct ns *ns)
{
if (!ns_is_enabled(ns)) {
if (have_netns()) {
ns->fd = open(ns->name, O_RDONLY);
} else {
ns->fd = -2; /* Remember that ns_enable_hook has been
called */
errno = -ENOTSUP;
}
if (!ns_is_enabled(ns)) {
zlog_err("Can not enable NS %u: %s!", ns->ns_id,
safe_strerror(errno));
return 0;
}
if (have_netns())
zlog_info("NS %u is associated with NETNS %s.",
ns->ns_id, ns->name);
zlog_info("NS %u is enabled.", ns->ns_id);
if (ns_master.ns_enable_hook)
(*ns_master.ns_enable_hook)(ns->ns_id, &ns->info);
}
return 1;
}
/*
* Disable a NS - that is, let the NS be unusable.
* The NS_DELETE_HOOK callback will be called to inform
* that they must release the resources in the NS.
*/
static void ns_disable(struct ns *ns)
{
if (ns_is_enabled(ns)) {
zlog_info("NS %u is to be disabled.", ns->ns_id);
if (ns_master.ns_disable_hook)
(*ns_master.ns_disable_hook)(ns->ns_id, &ns->info);
if (have_netns())
close(ns->fd);
ns->fd = -1;
}
}
/* Add a NS hook. Please add hooks before calling ns_init(). */
void ns_add_hook(int type, int (*func)(ns_id_t, void **))
{
switch (type) {
case NS_NEW_HOOK:
ns_master.ns_new_hook = func;
break;
case NS_DELETE_HOOK:
ns_master.ns_delete_hook = func;
break;
case NS_ENABLE_HOOK:
ns_master.ns_enable_hook = func;
break;
case NS_DISABLE_HOOK:
ns_master.ns_disable_hook = func;
break;
default:
break;
}
}
/*
* NS realization with NETNS
*/
static char *ns_netns_pathname(struct vty *vty, const char *name)
{
static char pathname[PATH_MAX];
char *result;
if (name[0] == '/') /* absolute pathname */
result = realpath(name, pathname);
else /* relevant pathname */
{
char tmp_name[PATH_MAX];
snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name);
result = realpath(tmp_name, pathname);
}
if (!result) {
vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno));
return NULL;
}
return pathname;
}
DEFUN_NOSH (ns_netns,
ns_netns_cmd,
"logical-router (1-65535) ns NAME",
"Enable a logical-router\n"
"Specify the logical-router indentifier\n"
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
int idx_number = 1;
int idx_name = 3;
ns_id_t ns_id = NS_DEFAULT;
struct ns *ns = NULL;
char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
if (!pathname)
return CMD_WARNING_CONFIG_FAILED;
ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
ns = ns_get(ns_id);
if (ns->name && strcmp(ns->name, pathname) != 0) {
vty_out(vty, "NS %u is already configured with NETNS %s\n",
ns->ns_id, ns->name);
return CMD_WARNING;
}
if (!ns->name)
ns->name = XSTRDUP(MTYPE_NS_NAME, pathname);
if (!ns_enable(ns)) {
vty_out(vty, "Can not associate NS %u with NETNS %s\n",
ns->ns_id, ns->name);
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
DEFUN (no_ns_netns,
no_ns_netns_cmd,
"no logical-router (1-65535) ns NAME",
NO_STR
"Enable a Logical-Router\n"
"Specify the Logical-Router identifier\n"
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
int idx_number = 2;
int idx_name = 4;
ns_id_t ns_id = NS_DEFAULT;
struct ns *ns = NULL;
char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
if (!pathname)
return CMD_WARNING_CONFIG_FAILED;
ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
ns = ns_lookup(ns_id);
if (!ns) {
vty_out(vty, "NS %u is not found\n", ns_id);
return CMD_SUCCESS;
}
if (ns->name && strcmp(ns->name, pathname) != 0) {
vty_out(vty, "Incorrect NETNS file name\n");
return CMD_WARNING_CONFIG_FAILED;
}
ns_disable(ns);
if (ns->name) {
XFREE(MTYPE_NS_NAME, ns->name);
ns->name = NULL;
}
return CMD_SUCCESS;
}
/* NS node. */
static struct cmd_node ns_node = {NS_NODE, "", /* NS node has no interface. */
1};
/* NS configuration write function. */
static int ns_config_write(struct vty *vty)
{
struct ns *ns;
int write = 0;
RB_FOREACH (ns, ns_head, &ns_tree) {
if (ns->ns_id == NS_DEFAULT || ns->name == NULL)
continue;
vty_out(vty, "logical-router %u netns %s\n", ns->ns_id,
ns->name);
write = 1;
}
return write;
}
/* Initialize NS module. */
void ns_init(void)
{
struct ns *default_ns;
/* The default NS always exists. */
default_ns = ns_get(NS_DEFAULT);
if (!default_ns) {
zlog_err("ns_init: failed to create the default NS!");
exit(1);
}
/* Set the default NS name. */
default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
/* Enable the default NS. */
if (!ns_enable(default_ns)) {
zlog_err("ns_init: failed to enable the default NS!");
exit(1);
}
if (have_netns()) {
/* Install NS commands. */
install_node(&ns_node, ns_config_write);
install_element(CONFIG_NODE, &ns_netns_cmd);
install_element(CONFIG_NODE, &no_ns_netns_cmd);
}
}
/* Terminate NS module. */
void ns_terminate(void)
{
struct ns *ns;
while ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL)
ns_delete(ns);
}
/* Create a socket for the NS. */
int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
{
struct ns *ns = ns_lookup(ns_id);
int ret = -1;
if (!ns_is_enabled(ns)) {
errno = ENOSYS;
return -1;
}
if (have_netns()) {
ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0;
if (ret >= 0) {
ret = socket(domain, type, protocol);
if (ns_id != NS_DEFAULT)
setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET);
}
} else
ret = socket(domain, type, protocol);
return ret;
}

View File

@ -24,16 +24,22 @@
#include "openbsd-tree.h"
#include "linklist.h"
#include "vty.h"
typedef u_int32_t ns_id_t;
/* the default NS ID */
#define NS_DEFAULT 0
#define NS_UNKNOWN UINT32_MAX
/* Default netns directory (Linux) */
#define NS_RUN_DIR "/var/run/netns"
#ifdef HAVE_NETNS
#define NS_DEFAULT_NAME "/proc/self/ns/net"
#else /* !HAVE_NETNS */
#define NS_DEFAULT_NAME "Default-logical-router"
#endif /* HAVE_NETNS */
struct ns {
RB_ENTRY(ns) entry;
@ -49,6 +55,9 @@ struct ns {
/* Master list of interfaces belonging to this NS */
struct list *iflist;
/* Back Pointer to VRF */
void *vrf_ctxt;
/* User data */
void *info;
};
@ -57,6 +66,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare)
extern struct ns_head ns_tree;
/*
* API for managing NETNS. eg from zebra daemon
* one want to manage the list of NETNS, etc...
*/
/*
* NS hooks
*/
@ -74,20 +88,82 @@ extern struct ns_head ns_tree;
* - param 2: the address of the user data pointer (the user data
* can be stored in or freed from there)
*/
extern void ns_add_hook(int, int (*)(ns_id_t, void **));
extern void ns_add_hook(int type, int (*)(struct ns *));
/*
* NS initializer/destructor
*/
/* Please add hooks before calling ns_init(). */
extern void ns_init(void);
extern void ns_terminate(void);
/* API to initialize NETNS managerment
* parameter is the default ns_id
*/
extern void ns_init_management(ns_id_t ns_id);
/*
* NS utilities
*/
/* Create a socket serving for the given NS */
extern int ns_socket(int, int, int, ns_id_t);
/* Create a socket serving for the given NS
*/
int ns_socket(int domain, int type, int protocol, ns_id_t ns_id);
/* return the path of the NETNS */
extern char *ns_netns_pathname(struct vty *vty, const char *name);
/* Parse and execute a function on all the NETNS */
extern void ns_walk_func(int (*func)(struct ns *));
/* API to get the NETNS name, from the ns pointer */
extern const char *ns_get_name(struct ns *ns);
/* only called from vrf ( when removing netns from vrf)
* or at VRF or logical router termination
*/
extern void ns_delete(struct ns *ns);
/* return > 0 if netns is available
* called by VRF to check netns backend is available for VRF
*/
extern int ns_have_netns(void);
/* API to get context information of a NS */
extern void *ns_info_lookup(ns_id_t ns_id);
/*
* NS init routine
* should be called from backendx
*/
extern void ns_init(void);
/* API to retrieve default NS */
extern ns_id_t ns_get_default_id(void);
#define NS_DEFAULT ns_get_default_id()
/* API that can be used to change from NS */
extern int ns_switchback_to_initial(void);
extern int ns_switch_to_netns(const char *netns_name);
/*
* NS handling routines.
* called by modules that use NS backend
*/
/* API to search for already present NETNS */
extern struct ns *ns_lookup(ns_id_t ns_id);
extern struct ns *ns_lookup_name(const char *name);
/* API to handle NS : creation, enable, disable
* for enable, a callback function is passed as parameter
* the callback belongs to the module that uses NS as backend
* upon enabling the NETNS, the upper layer is informed
*/
extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *));
extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id);
extern void ns_disable(struct ns *ns);
#endif /*_ZEBRA_NS_H*/

View File

@ -239,6 +239,7 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
#define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
#define IPV4_CLASS_DE(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
#define IPV4_MC_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffffff00) == 0xe0000000)
/* Max bit/byte length of IPv6 address. */
#define IPV6_MAX_BYTELEN 16

View File

@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \
lib/module.c \
lib/network.c \
lib/nexthop.c \
lib/ns.c \
lib/netns_linux.c \
lib/netns_other.c \
lib/openbsd-tree.c \
lib/pid_output.c \
lib/plist.c \
@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \
lib/wheel.c \
lib/workqueue.c \
lib/zclient.c \
lib/logicalrouter.c \
# end
lib/plist_clippy.c: $(CLIPPY_DEPS)
@ -158,6 +160,7 @@ pkginclude_HEADERS += \
lib/zassert.h \
lib/zclient.h \
lib/zebra.h \
lib/logicalrouter.h \
# end
nodist_pkginclude_HEADERS += \

383
lib/vrf.c
View File

@ -21,6 +21,9 @@
#include <zebra.h>
/* for basename */
#include <libgen.h>
#include "if.h"
#include "vrf.h"
#include "vrf_int.h"
@ -29,6 +32,10 @@
#include "log.h"
#include "memory.h"
#include "command.h"
#include "ns.h"
/* default VRF ID value used when VRF backend is not NETNS */
#define VRF_DEFAULT_INTERNAL 0
DEFINE_MTYPE_STATIC(LIB, VRF, "VRF")
DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map")
@ -44,6 +51,8 @@ RB_GENERATE(vrf_name_head, vrf, name_entry, vrf_name_compare);
struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id);
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
static int vrf_backend;
/*
* Turn on/off debug code
* for vrf.
@ -61,7 +70,6 @@ struct vrf_master {
};
static int vrf_is_enabled(struct vrf *vrf);
static void vrf_disable(struct vrf *vrf);
/* VRF list existance check by name. */
struct vrf *vrf_lookup_by_name(const char *name)
@ -81,6 +89,54 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b)
return strcmp(a->name, b->name);
}
/* if ns_id is different and not VRF_UNKNOWN,
* then update vrf identifier, and enable VRF
*/
static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr)
{
ns_id_t vrf_id = (vrf_id_t)ns_id;
vrf_id_t old_vrf_id;
struct vrf *vrf = (struct vrf *)opaqueptr;
if (!vrf)
return;
old_vrf_id = vrf->vrf_id;
if (vrf_id == vrf->vrf_id)
return;
if (vrf->vrf_id != VRF_UNKNOWN)
RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
vrf->vrf_id = vrf_id;
RB_INSERT(vrf_id_head, &vrfs_by_id, vrf);
if (old_vrf_id == VRF_UNKNOWN)
vrf_enable((struct vrf *)vrf);
}
int vrf_switch_to_netns(vrf_id_t vrf_id)
{
char *name;
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
/* VRF is default VRF. silently ignore */
if (!vrf || vrf->vrf_id == VRF_DEFAULT)
return 0;
/* VRF has no NETNS backend. silently ignore */
if (vrf->data.l.netns_name[0] == '\0')
return 0;
name = ns_netns_pathname(NULL, vrf->data.l.netns_name);
if (debug_vrf)
zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id);
return ns_switch_to_netns(name);
}
int vrf_switchback_to_initial(void)
{
int ret = ns_switchback_to_initial();
if (ret == 0 && debug_vrf)
zlog_debug("VRF_SWITCHBACK");
return ret;
}
/* Get a VRF. If not found, create one.
* Arg:
* name - The name of the vrf. May be NULL if unknown.
@ -94,7 +150,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
int new = 0;
if (debug_vrf)
zlog_debug("VRF_GET: %s(%u)", name, vrf_id);
zlog_debug("VRF_GET: %s(%u)",
name == NULL ? "(NULL)" : name, vrf_id);
/* Nothing to see, move along here */
if (!name && vrf_id == VRF_UNKNOWN)
@ -109,8 +166,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
if (vrf == NULL) {
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
vrf->vrf_id = VRF_UNKNOWN;
RB_INIT(if_name_head, &vrf->ifaces_by_name);
RB_INIT(if_index_head, &vrf->ifaces_by_index);
QOBJ_REG(vrf, vrf);
new = 1;
@ -134,7 +189,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
strlcpy(vrf->name, name, sizeof(vrf->name));
RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
}
if (new &&vrf_master.vrf_new_hook)
(*vrf_master.vrf_new_hook)(vrf);
@ -219,7 +273,7 @@ int vrf_enable(struct vrf *vrf)
* The VRF_DELETE_HOOK callback will be called to inform
* that they must release the resources in the VRF.
*/
static void vrf_disable(struct vrf *vrf)
void vrf_disable(struct vrf *vrf)
{
if (!vrf_is_enabled(vrf))
return;
@ -385,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
{
struct vrf *default_vrf;
/* initialise NS, in case VRF backend if NETNS */
ns_init();
if (debug_vrf)
zlog_debug("%s: Initializing VRF subsystem",
__PRETTY_FUNCTION__);
@ -419,12 +475,17 @@ void vrf_terminate(void)
zlog_debug("%s: Shutting down vrf subsystem",
__PRETTY_FUNCTION__);
while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) {
while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) {
vrf = RB_ROOT(vrf_id_head, &vrfs_by_id);
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
}
while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) {
while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) {
vrf = RB_ROOT(vrf_name_head, &vrfs_by_name);
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
@ -432,15 +493,150 @@ void vrf_terminate(void)
}
/* Create a socket for the VRF. */
int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id)
int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
char *interfacename)
{
int ret = -1;
int ret, save_errno, ret2;
ret = vrf_switch_to_netns(vrf_id);
if (ret < 0)
zlog_err("%s: Can't switch to VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
ret = socket(domain, type, protocol);
save_errno = errno;
ret2 = vrf_switchback_to_initial();
if (ret2 < 0)
zlog_err("%s: Can't switchback from VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
errno = save_errno;
if (ret <= 0)
return ret;
ret2 = vrf_bind(vrf_id, ret, interfacename);
if (ret2 < 0) {
close(ret);
ret = ret2;
}
return ret;
}
int vrf_is_backend_netns(void)
{
return (vrf_backend == VRF_BACKEND_NETNS);
}
int vrf_get_backend(void)
{
return vrf_backend;
}
void vrf_configure_backend(int vrf_backend_netns)
{
vrf_backend = vrf_backend_netns;
}
int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf)
{
struct vrf *vrfp;
if (strlen(vrfname) > VRF_NAMSIZ) {
if (vty)
vty_out(vty,
"%% VRF name %s invalid: length exceeds %d bytes\n",
vrfname, VRF_NAMSIZ);
else
zlog_warn(
"%% VRF name %s invalid: length exceeds %d bytes\n",
vrfname, VRF_NAMSIZ);
return CMD_WARNING_CONFIG_FAILED;
}
vrfp = vrf_get(VRF_UNKNOWN, vrfname);
if (vty)
VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
if (vrf)
*vrf = vrfp;
return CMD_SUCCESS;
}
int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
char *pathname, ns_id_t ns_id)
{
struct ns *ns = NULL;
if (!vrf)
return CMD_WARNING_CONFIG_FAILED;
if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) {
if (vty)
vty_out(vty,
"VRF %u is already configured with VRF %s\n",
vrf->vrf_id, vrf->name);
else
zlog_warn("VRF %u is already configured with VRF %s\n",
vrf->vrf_id, vrf->name);
return CMD_WARNING_CONFIG_FAILED;
}
if (vrf->ns_ctxt != NULL) {
ns = (struct ns *) vrf->ns_ctxt;
if (ns && 0 != strcmp(ns->name, pathname)) {
if (vty)
vty_out(vty,
"VRF %u already configured with NETNS %s\n",
vrf->vrf_id, ns->name);
else
zlog_warn(
"VRF %u already configured with NETNS %s",
vrf->vrf_id, ns->name);
return CMD_WARNING_CONFIG_FAILED;
}
}
ns = ns_lookup_name(pathname);
if (ns && ns->vrf_ctxt) {
struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt;
if (vrf2 == vrf)
return CMD_SUCCESS;
if (vty)
vty_out(vty, "NS %s is already configured"
" with VRF %u(%s)\n",
ns->name, vrf2->vrf_id, vrf2->name);
else
zlog_warn("NS %s is already configured with VRF %u(%s)",
ns->name, vrf2->vrf_id, vrf2->name);
return CMD_WARNING_CONFIG_FAILED;
}
ns = ns_get_created(ns, pathname, ns_id);
ns->vrf_ctxt = (void *)vrf;
vrf->ns_ctxt = (void *)ns;
/* update VRF netns NAME */
if (vrf)
strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ);
if (!ns_enable(ns, vrf_update_vrf_id)) {
if (vty)
vty_out(vty, "Can not associate NS %u with NETNS %s\n",
ns->ns_id, ns->name);
else
zlog_warn("Can not associate NS %u with NETNS %s",
ns->ns_id, ns->name);
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
int vrf_is_mapped_on_netns(vrf_id_t vrf_id)
{
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
if (!vrf || vrf->data.l.netns_name[0] == '\0')
return 0;
if (vrf->vrf_id == VRF_DEFAULT)
return 0;
return 1;
}
/* vrf CLI commands */
DEFUN_NOSH (vrf,
vrf_cmd,
@ -450,21 +646,8 @@ DEFUN_NOSH (vrf,
{
int idx_name = 1;
const char *vrfname = argv[idx_name]->arg;
struct vrf *vrfp;
if (strlen(vrfname) > VRF_NAMSIZ) {
vty_out(vty,
"%% VRF name %s is invalid: length exceeds "
"%d characters\n",
vrfname, VRF_NAMSIZ);
return CMD_WARNING_CONFIG_FAILED;
}
vrfp = vrf_get(VRF_UNKNOWN, vrfname);
VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
return CMD_SUCCESS;
return vrf_handler_create(vty, vrfname, NULL);
}
DEFUN_NOSH (no_vrf,
@ -500,6 +683,55 @@ DEFUN_NOSH (no_vrf,
struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
DEFUN_NOSH (vrf_netns,
vrf_netns_cmd,
"netns NAME",
"Attach VRF to a Namespace\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
int idx_name = 1;
char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
VTY_DECLVAR_CONTEXT(vrf, vrf);
if (!pathname)
return CMD_WARNING_CONFIG_FAILED;
return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
}
DEFUN (no_vrf_netns,
no_vrf_netns_cmd,
"no netns [NAME]",
NO_STR
"Detach VRF from a Namespace\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
struct ns *ns = NULL;
VTY_DECLVAR_CONTEXT(vrf, vrf);
if (!vrf_is_backend_netns()) {
vty_out(vty, "VRF backend is not Netns. Aborting\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (!vrf->ns_ctxt) {
vty_out(vty, "VRF %s(%u) is not configured with NetNS\n",
vrf->name, vrf->vrf_id);
return CMD_WARNING_CONFIG_FAILED;
}
ns = (struct ns *)vrf->ns_ctxt;
ns->vrf_ctxt = NULL;
vrf_disable(vrf);
/* vrf ID from VRF is necessary for Zebra
* so that propagate to other clients is done
*/
ns_delete(ns);
vrf->ns_ctxt = NULL;
return CMD_SUCCESS;
}
/*
* Debug CLI for vrf's
*/
@ -552,4 +784,105 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty))
install_element(CONFIG_NODE, &no_vrf_cmd);
install_node(&vrf_node, writefunc);
install_default(VRF_NODE);
if (vrf_is_backend_netns() && ns_have_netns()) {
/* Install NS commands. */
install_element(VRF_NODE, &vrf_netns_cmd);
install_element(VRF_NODE, &no_vrf_netns_cmd);
}
}
vrf_id_t vrf_get_default_id(void)
{
struct vrf *vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME);
if (vrf)
return vrf->vrf_id;
if (vrf_is_backend_netns())
return ns_get_default_id();
else
return VRF_DEFAULT_INTERNAL;
}
int vrf_bind(vrf_id_t vrf_id, int fd, char *name)
{
int ret = 0;
if (fd < 0 || name == NULL)
return fd;
if (vrf_is_mapped_on_netns(vrf_id))
return fd;
#ifdef SO_BINDTODEVICE
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name,
strlen(name));
if (ret < 0)
zlog_debug("bind to interface %s failed, errno=%d",
name, errno);
#endif /* SO_BINDTODEVICE */
return ret;
}
int vrf_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res, vrf_id_t vrf_id)
{
int ret, ret2, save_errno;
ret = vrf_switch_to_netns(vrf_id);
if (ret < 0)
zlog_err("%s: Can't switch to VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
ret = getaddrinfo(node, service, hints, res);
save_errno = errno;
ret2 = vrf_switchback_to_initial();
if (ret2 < 0)
zlog_err("%s: Can't switchback from VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
errno = save_errno;
return ret;
}
int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params)
{
int ret, saved_errno, rc;
ret = vrf_switch_to_netns(vrf_id);
if (ret < 0) {
zlog_err("%s: Can't switch to VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
return 0;
}
rc = ioctl(d, request, params);
saved_errno = errno;
ret = vrf_switchback_to_initial();
if (ret < 0)
zlog_err("%s: Can't switchback from VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
errno = saved_errno;
return rc;
}
int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
char *interfacename)
{
int ret, save_errno, ret2;
ret = vrf_switch_to_netns(vrf_id);
if (ret < 0)
zlog_err("%s: Can't switch to VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
ret = sockunion_socket(su);
save_errno = errno;
ret2 = vrf_switchback_to_initial();
if (ret2 < 0)
zlog_err("%s: Can't switchback from VRF %u (%s)",
__func__, vrf_id, safe_strerror(errno));
errno = save_errno;
if (ret <= 0)
return ret;
ret2 = vrf_bind(vrf_id, ret, interfacename);
if (ret2 < 0) {
close(ret);
ret = ret2;
}
return ret;
}

View File

@ -26,12 +26,9 @@
#include "linklist.h"
#include "qobj.h"
#include "vty.h"
/* The default NS ID */
#define NS_DEFAULT 0
#include "ns.h"
/* The default VRF ID */
#define VRF_DEFAULT 0
#define VRF_UNKNOWN UINT32_MAX
/* Pending: May need to refine this. */
@ -42,6 +39,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX };
#endif
#define VRF_NAMSIZ 36
#define NS_NAMSIZ 16
#define VRF_DEFAULT_NAME "Default-IP-Routing-Table"
@ -60,6 +58,7 @@ struct vrf_data {
union {
struct {
uint32_t table_id;
char netns_name[NS_NAMSIZ];
} l;
};
};
@ -88,6 +87,9 @@ struct vrf {
/* The table_id from the kernel */
struct vrf_data data;
/* Back pointer to namespace context */
void *ns_ctxt;
QOBJ_FIELDS
};
RB_HEAD(vrf_id_head, vrf);
@ -96,6 +98,9 @@ RB_HEAD(vrf_name_head, vrf);
RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
DECLARE_QOBJ_TYPE(vrf)
/* Allow VRF with netns as backend */
#define VRF_BACKEND_VRF_LITE 0
#define VRF_BACKEND_NETNS 1
extern struct vrf_id_head vrfs_by_id;
extern struct vrf_name_head vrfs_by_name;
@ -195,17 +200,85 @@ extern void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
*/
extern void vrf_terminate(void);
/*
* Utilities to create networks objects,
* or call network operations
*/
/* Create a socket serving for the given VRF */
extern int vrf_socket(int domain, int type,
int protocol, vrf_id_t vrf_id,
char *name);
extern int vrf_sockunion_socket(const union sockunion *su,
vrf_id_t vrf_id, char *name);
extern int vrf_bind(vrf_id_t vrf_id, int fd, char *name);
/* VRF ioctl operations */
extern int vrf_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res, vrf_id_t vrf_id);
extern int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *args);
/* function called by macro VRF_DEFAULT
* to get the default VRF_ID
*/
extern vrf_id_t vrf_get_default_id(void);
/* The default VRF ID */
#define VRF_DEFAULT vrf_get_default_id()
/* VRF is mapped on netns or not ? */
int vrf_is_mapped_on_netns(vrf_id_t vrf_id);
/* VRF switch from NETNS */
extern int vrf_switch_to_netns(vrf_id_t vrf_id);
extern int vrf_switchback_to_initial(void);
/*
* VRF backend routines
* should be called from zebra only
*/
/* VRF vty command initialisation
*/
extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
/* VRF vty debugging
*/
extern void vrf_install_commands(void);
/*
* VRF utilities
*/
/* Create a socket serving for the given VRF */
extern int vrf_socket(int, int, int, vrf_id_t);
/*
* VRF Debugging
/* API for configuring VRF backend
* should be called from zebra only
*/
extern void vrf_install_commands(void);
extern void vrf_configure_backend(int vrf_backend_netns);
extern int vrf_get_backend(void);
extern int vrf_is_backend_netns(void);
/* API to create a VRF. either from vty
* or through discovery
*/
extern int vrf_handler_create(struct vty *vty,
const char *name,
struct vrf **vrf);
/* API to associate a VRF with a NETNS.
* called either from vty or through discovery
* 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);
/* used internally to enable or disable VRF.
* Notify a change in the VRF ID of the VRF
*/
extern void vrf_disable(struct vrf *vrf);
extern int vrf_enable(struct vrf *vrf);
#endif /*_ZEBRA_VRF_H*/

View File

@ -363,7 +363,7 @@ static int zebra_hello_send(struct zclient *zclient)
return 0;
}
void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id,
void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi,
mpls_label_t label, enum lsp_types_t ltype)
{
struct stream *s;
@ -373,6 +373,7 @@ void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id,
zclient_create_header(s, ZEBRA_VRF_LABEL, vrf_id);
stream_putl(s, label);
stream_putc(s, afi);
stream_putc(s, ltype);
stream_putw_at(s, 0, stream_get_endp(s));
zclient_send_message(zclient);
@ -1209,14 +1210,20 @@ stream_failure:
}
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
uint32_t *tableid,
enum zapi_route_notify_owner *note)
{
uint32_t t;
STREAM_GET(note, s, sizeof(*note));
STREAM_GETC(s, p->family);
STREAM_GETC(s, p->prefixlen);
STREAM_GET(&p->u.prefix, s,
PSIZE(p->prefixlen));
prefix_blen(p));
STREAM_GETL(s, t);
*tableid = t;
return true;
@ -1401,8 +1408,8 @@ static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
/* Lookup/create vrf by vrf_id. */
vrf = vrf_get(vrf_id, vrfname_tmp);
vrf->data = data;
vrf->data.l.table_id = data.l.table_id;
memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ);
vrf_enable(vrf);
}
@ -2362,9 +2369,9 @@ static int zclient_read(struct thread *thread)
vrf_id);
break;
case ZEBRA_ROUTE_NOTIFY_OWNER:
if (zclient->notify_owner)
(*zclient->notify_owner)(command, zclient,
length, vrf_id);
if (zclient->route_notify_owner)
(*zclient->route_notify_owner)(command, zclient, length,
vrf_id);
break;
default:
break;

View File

@ -213,8 +213,8 @@ struct zclient {
int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);
int (*notify_owner)(int command, struct zclient *zclient,
uint16_t length, vrf_id_t vrf_id);
int (*route_notify_owner)(int command, struct zclient *zclient,
uint16_t length, vrf_id_t vrf_id);
};
/* Zebra API message flag. */
@ -390,9 +390,15 @@ extern void redist_del_instance(struct redist_proto *, u_short);
* label for lookup. If you pass in MPLS_LABEL_NONE
* we will cause a delete action and remove this label pop
* operation.
*
* The underlying AF_MPLS doesn't care about afi's
* but we can make the zebra_vrf keep track of what
* we have installed and play some special games
* to get them both installed.
*/
extern void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id,
mpls_label_t label, enum lsp_types_t ltype);
afi_t afi, mpls_label_t label,
enum lsp_types_t ltype);
extern void zclient_send_reg_requests(struct zclient *, vrf_id_t);
extern void zclient_send_dereg_requests(struct zclient *, vrf_id_t);
@ -507,6 +513,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command,
extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *);
extern int zapi_route_decode(struct stream *, struct zapi_route *);
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
uint32_t *tableid,
enum zapi_route_notify_owner *note);
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
extern bool zapi_nexthop_update_decode(struct stream *s,

View File

@ -402,7 +402,7 @@ extern const char *zserv_command_string(unsigned int command);
#define strmatch(a,b) (!strcmp((a), (b)))
/* Zebra message flags */
#define ZEBRA_FLAG_INTERNAL 0x01
#define ZEBRA_FLAG_ALLOW_RECURSION 0x01
#define ZEBRA_FLAG_SELFROUTE 0x02
#define ZEBRA_FLAG_IBGP 0x08
#define ZEBRA_FLAG_SELECTED 0x10

View File

@ -114,7 +114,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix
SET_FLAG(api.flags, ZEBRA_FLAG_FIB_OVERRIDE);
break;
}
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api.nexthop_num = 1;

View File

@ -99,6 +99,8 @@ struct ospf6_area {
/* Time stamps. */
struct timeval ts_spf; /* SPF calculation time stamp. */
uint32_t full_nbrs; /* Fully adjacent neighbors. */
};
#define OSPF6_AREA_ENABLE 0x01

View File

@ -193,7 +193,11 @@ static void ospf6_neighbor_state_change(u_char next_state,
if (prev_state == OSPF6_NEIGHBOR_LOADING &&
next_state == OSPF6_NEIGHBOR_FULL) {
OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if);
on->ospf6_if->area->full_nbrs++;
}
if (prev_state == OSPF6_NEIGHBOR_FULL)
on->ospf6_if->area->full_nbrs--;
}
if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE

View File

@ -1029,18 +1029,21 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area,
/* Fill Larger LSA Payload */
end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa);
if (rtr_lsa) {
if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
/* Append first Link State ID LSA */
lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header;
memcpy(new_header, lsa_header,
ntohs(lsa_header->length));
/* Assign new lsa length as aggregated length. */
((struct ospf6_lsa_header *)new_header)->length =
htons(total_lsa_length);
new_header += ntohs(lsa_header->length);
num_lsa--;
}
/*
* We assume at this point in time that rtr_lsa is
* a valid pointer.
*/
assert(rtr_lsa);
if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
/* Append first Link State ID LSA */
lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header;
memcpy(new_header, lsa_header, ntohs(lsa_header->length));
/* Assign new lsa length as aggregated length. */
((struct ospf6_lsa_header *)new_header)->length =
htons(total_lsa_length);
new_header += ntohs(lsa_header->length);
num_lsa--;
}
/* Print LSA Name */

View File

@ -335,6 +335,8 @@ DEFUN(ospf6_router_id,
int ret;
const char *router_id_str;
u_int32_t router_id;
struct ospf6_area *oa;
struct listnode *node;
argv_find(argv, argc, "A.B.C.D", &idx);
router_id_str = argv[idx]->arg;
@ -346,8 +348,17 @@ DEFUN(ospf6_router_id,
}
o->router_id_static = router_id;
if (o->router_id == 0)
o->router_id = router_id;
for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
if (oa->full_nbrs) {
vty_out(vty,
"For this router-id change to take effect,"
" save config and restart ospf6d\n");
return CMD_SUCCESS;
}
}
o->router_id = router_id;
return CMD_SUCCESS;
}
@ -360,8 +371,22 @@ DEFUN(no_ospf6_router_id,
V4NOTATION_STR)
{
VTY_DECLVAR_CONTEXT(ospf6, o);
struct ospf6_area *oa;
struct listnode *node;
o->router_id_static = 0;
for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
if (oa->full_nbrs) {
vty_out(vty,
"For this router-id change to take effect,"
" save config and restart ospf6d\n");
return CMD_SUCCESS;
}
}
o->router_id = 0;
if (o->router_id_zebra.s_addr)
o->router_id = (uint32_t)o->router_id_zebra.s_addr;
return CMD_SUCCESS;
}
@ -523,6 +548,10 @@ DEFUN (ospf6_distance_ospf6,
VTY_DECLVAR_CONTEXT(ospf6, o);
int idx = 0;
o->distance_intra = 0;
o->distance_inter = 0;
o->distance_external = 0;
if (argv_find(argv, argc, "intra-area", &idx))
o->distance_intra = atoi(argv[idx + 1]->arg);
idx = 0;

View File

@ -32,6 +32,8 @@ struct ospf6 {
/* static router id */
u_int32_t router_id_static;
struct in_addr router_id_zebra;
/* start time */
struct timeval starttime;

View File

@ -46,8 +46,6 @@ unsigned char conf_debug_ospf6_zebra = 0;
/* information about zebra. */
struct zclient *zclient = NULL;
struct in_addr router_id_zebra;
/* Router-id update message from zebra. */
static int ospf6_router_id_update_zebra(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
@ -56,13 +54,14 @@ static int ospf6_router_id_update_zebra(int command, struct zclient *zclient,
struct ospf6 *o = ospf6;
zebra_router_id_update_read(zclient->ibuf, &router_id);
router_id_zebra = router_id.u.prefix4;
if (o == NULL)
return 0;
o->router_id_zebra = router_id.u.prefix4;
if (o->router_id == 0)
o->router_id = (u_int32_t)router_id_zebra.s_addr;
o->router_id = (uint32_t)o->router_id_zebra.s_addr;
return 0;
}

View File

@ -175,7 +175,7 @@ void ospf_ext_term(void)
{
if ((OspfEXT.scope != OSPF_OPAQUE_AREA_LSA)
|| (OspfEXT.scope != OSPF_OPAQUE_AS_LSA))
&& (OspfEXT.scope != OSPF_OPAQUE_AS_LSA))
zlog_warn(
"EXT: Unable to unregister Extended Prefix "
"Opaque LSA functions: Wrong scope!");
@ -334,10 +334,12 @@ static void set_prefix_sid(struct ext_itf *exti, uint8_t algorithm,
/* Set Label or Index value */
if (value_type == SID_LABEL) {
TLV_LEN(exti->node_sid) = htons(SID_LABEL_SIZE);
TLV_LEN(exti->node_sid) =
htons(SID_LABEL_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE));
exti->node_sid.value = htonl(SET_LABEL(value));
} else {
TLV_LEN(exti->node_sid) = htons(SID_INDEX_SIZE);
TLV_LEN(exti->node_sid) =
htons(SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE));
exti->node_sid.value = htonl(value);
}
@ -370,11 +372,13 @@ static void set_adj_sid(struct ext_itf *exti, bool backup, uint32_t value,
/* Adjust Length, Flags and Value depending on the type of Label */
if (value_type == SID_LABEL) {
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
TLV_LEN(exti->adj_sid[index]) = htons(SID_LABEL_SIZE);
TLV_LEN(exti->adj_sid[index]) =
htons(SID_LABEL_SIZE(EXT_SUBTLV_ADJ_SID_SIZE));
exti->adj_sid[index].value = htonl(SET_LABEL(value));
} else {
UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
TLV_LEN(exti->adj_sid[index]) = htons(SID_INDEX_SIZE);
TLV_LEN(exti->adj_sid[index]) =
htons(SID_INDEX_SIZE(EXT_SUBTLV_ADJ_SID_SIZE));
exti->adj_sid[index].value = htonl(value);
}
@ -402,7 +406,7 @@ static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value,
}
/* Set Header */
TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_ADJ_SID);
TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_LAN_ADJ_SID);
/* Only Local ADJ-SID is supported for the moment */
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
@ -410,11 +414,13 @@ static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value,
/* Adjust Length, Flags and Value depending on the type of Label */
if (value_type == SID_LABEL) {
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
TLV_LEN(exti->lan_sid[index]) = htons(SID_LABEL_SIZE);
TLV_LEN(exti->lan_sid[index]) =
htons(SID_LABEL_SIZE(EXT_SUBTLV_PREFIX_RANGE_SIZE));
exti->lan_sid[index].value = htonl(SET_LABEL(value));
} else {
UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
TLV_LEN(exti->lan_sid[index]) = htons(SID_INDEX_SIZE);
TLV_LEN(exti->lan_sid[index]) =
htons(SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_RANGE_SIZE));
exti->lan_sid[index].value = htonl(value);
}
@ -906,7 +912,7 @@ static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti)
/* Build LSA body for an Extended Link TLV with Adj. SID */
build_tlv_header(s, &exti->link.header);
stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE);
/* then add Ajacency SubTLVs */
/* then add Adjacency SubTLVs */
build_tlv(s, &exti->adj_sid[1].header);
build_tlv(s, &exti->adj_sid[0].header);
@ -921,8 +927,8 @@ static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti)
/* Build LSA body for an Extended Link TLV with LAN SID */
build_tlv_header(s, &exti->link.header);
stream_put(s, &exti->link.header, EXT_TLV_LINK_SIZE);
/* then add LAN-Ajacency SubTLVs */
stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE);
/* then add LAN-Adjacency SubTLVs */
build_tlv(s, &exti->lan_sid[1].header);
build_tlv(s, &exti->lan_sid[0].header);
}
@ -1671,8 +1677,9 @@ static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty,
vty_out(vty,
" LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: "
"0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: "
"%s\n\tLabel: %u\n",
"%s\n\t%s: %u\n",
ntohs(top->header.length), top->flags, top->mtid, top->weight,
inet_ntoa(top->neighbor_id),
CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
: "Index",
CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)

View File

@ -563,6 +563,7 @@ int ospf_flood_through_area(struct ospf_area *area, struct ospf_neighbor *inbr,
struct ospf_interface *oi;
int lsa_ack_flag = 0;
assert(area);
/* All other types are specific to a single area (Area A). The
eligible interfaces are all those interfaces attaching to the
Area A. If Area A is the backbone, this includes all the virtual

View File

@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
return ret;
}
int ospf_bind_vrfdevice(struct ospf *ospf, int ospf_sock)
{
int ret = 0;
#ifdef SO_BINDTODEVICE
if (ospf && ospf->vrf_id != VRF_DEFAULT &&
ospf->vrf_id != VRF_UNKNOWN) {
ret = setsockopt(ospf_sock, SOL_SOCKET, SO_BINDTODEVICE,
ospf->name,
strlen(ospf->name));
if (ret < 0) {
int save_errno = errno;
zlog_warn("%s: Could not setsockopt SO_BINDTODEVICE %s",
__PRETTY_FUNCTION__,
safe_strerror(save_errno));
}
}
#endif
return ret;
}
int ospf_sock_init(struct ospf *ospf)
{
int ospf_sock;
int ret, hincl = 1;
int bufsize = (8 * 1024 * 1024);
/* silently ignore. already done */
if (ospf->fd > 0)
return -1;
if (ospf->vrf_id == VRF_UNKNOWN) {
/* silently return since VRF is not ready */
return -1;
}
if (ospfd_privs.change(ZPRIVS_RAISE)) {
zlog_err("ospf_sock_init: could not raise privs, %s",
safe_strerror(errno));
}
ospf_sock = socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP);
ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP,
ospf->vrf_id, ospf->name);
if (ospf_sock < 0) {
int save_errno = errno;
@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf)
exit(1);
}
ret = ospf_bind_vrfdevice(ospf, ospf_sock);
if (ret < 0) {
close(ospf_sock);
goto out;
}
#ifdef IP_HDRINCL
/* we will include IP header with packet */
ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl,

View File

@ -30,6 +30,5 @@ extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t);
extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t);
extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t);
extern int ospf_sock_init(struct ospf *ospf);
extern int ospf_bind_vrfdevice(struct ospf *, int);
#endif /* _ZEBRA_OSPF_NETWORK_H */

View File

@ -75,6 +75,7 @@ static void ospf_opaque_funclist_init(void);
static void ospf_opaque_funclist_term(void);
static void free_opaque_info_per_type(void *val);
static void free_opaque_info_per_id(void *val);
static void free_opaque_info_owner(void *val);
static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
@ -439,9 +440,11 @@ void ospf_delete_opaque_functab(u_char lsa_type, u_char opaque_type)
if (functab->opaque_type == opaque_type) {
/* Cleanup internal control information, if it
* still remains. */
if (functab->oipt != NULL)
if (functab->oipt != NULL) {
free_opaque_info_per_type(
functab->oipt);
free_opaque_info_owner(functab->oipt);
}
/* Dequeue listnode entry from the list. */
listnode_delete(funclist, functab);
@ -572,6 +575,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
top = ospf_lookup_by_vrf_id(new->vrf_id);
if (new->area != NULL && (top = new->area->ospf) == NULL) {
free_opaque_info_per_type((void *)oipt);
free_opaque_info_owner(oipt);
oipt = NULL;
goto out; /* This case may not exist. */
}
@ -583,6 +587,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
"register_opaque_info_per_type: Unexpected LSA-type(%u)",
new->data->type);
free_opaque_info_per_type((void *)oipt);
free_opaque_info_owner(oipt);
oipt = NULL;
goto out; /* This case may not exist. */
}
@ -600,6 +605,35 @@ out:
return oipt;
}
/* Remove "oipt" from its owner's self-originated LSA list. */
static void free_opaque_info_owner(void *val)
{
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
switch (oipt->lsa_type) {
case OSPF_OPAQUE_LINK_LSA: {
struct ospf_interface *oi =
(struct ospf_interface *)(oipt->owner);
listnode_delete(oi->opaque_lsa_self, oipt);
break;
}
case OSPF_OPAQUE_AREA_LSA: {
struct ospf_area *area = (struct ospf_area *)(oipt->owner);
listnode_delete(area->opaque_lsa_self, oipt);
break;
}
case OSPF_OPAQUE_AS_LSA: {
struct ospf *top = (struct ospf *)(oipt->owner);
listnode_delete(top->opaque_lsa_self, oipt);
break;
}
default:
zlog_warn("free_opaque_info_owner: Unexpected LSA-type(%u)",
oipt->lsa_type);
break; /* This case may not exist. */
}
}
static void free_opaque_info_per_type(void *val)
{
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;

View File

@ -3897,6 +3897,10 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi,
zlog_debug("listcount = %d, [%s]dst %s", listcount(update),
IF_NAME(oi), inet_ntoa(addr));
/* Check that we have really something to process */
if (listcount(update) == 0)
return;
op = ospf_ls_upd_packet_new(update, oi);
/* Prepare OSPF common header. */

View File

@ -337,6 +337,7 @@ static struct route_map_rule_cmd route_match_tag_cmd = {
};
struct ospf_metric {
enum { metric_increment, metric_decrement, metric_absolute } type;
bool used;
u_int32_t metric;
};
@ -356,8 +357,19 @@ static route_map_result_t route_set_metric(void *rule, struct prefix *prefix,
ei = object;
/* Set metric out value. */
if (metric->used)
if (!metric->used)
return RMAP_OKAY;
if (metric->type == metric_increment)
ei->route_map_set.metric += metric->metric;
if (metric->type == metric_decrement)
ei->route_map_set.metric -= metric->metric;
if (metric->type == metric_absolute)
ei->route_map_set.metric = metric->metric;
if ((signed int)ei->route_map_set.metric < 1)
ei->route_map_set.metric = -1;
if (ei->route_map_set.metric > OSPF_LS_INFINITY)
ei->route_map_set.metric = OSPF_LS_INFINITY;
}
return RMAP_OKAY;
}
@ -370,23 +382,28 @@ static void *route_set_metric_compile(const char *arg)
metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_int32_t));
metric->used = false;
/* OSPF doesn't support the +/- in
set metric <+/-metric> check
Ignore the +/- component */
if (!all_digit(arg)) {
if ((arg[0] == '+' || arg[0] == '-') && all_digit(arg + 1)) {
zlog_warn("OSPF does not support 'set metric +/-'");
arg++;
} else {
if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt"))
zlog_warn(
"OSPF does not support 'set metric +rtt / -rtt'");
if (all_digit(arg))
metric->type = metric_absolute;
return metric;
}
if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) {
zlog_warn("OSPF does not support 'set metric +rtt / -rtt'");
return metric;
}
if ((arg[0] == '+') && all_digit(arg + 1)) {
metric->type = metric_increment;
arg++;
}
if ((arg[0] == '-') && all_digit(arg + 1)) {
metric->type = metric_decrement;
arg++;
}
metric->metric = strtoul(arg, NULL, 10);
metric->used = true;
if (metric->metric)
metric->used = true;
return metric;
}

View File

@ -149,14 +149,6 @@ static struct sr_node *sr_node_new(struct in_addr *rid)
new->ext_link->del = del_sr_link;
new->ext_prefix->del = del_sr_pref;
/* Check if list are correctly created */
if (new->ext_link == NULL || new->ext_prefix == NULL) {
list_delete_original(new->ext_link);
list_delete_original(new->ext_prefix);
XFREE(MTYPE_OSPF_SR_PARAMS, new);
return NULL;
}
IPV4_ADDR_COPY(&new->adv_router, rid);
new->neighbor = NULL;
new->instance = 0;
@ -440,7 +432,7 @@ static struct ospf_path *get_nexthop_by_addr(struct ospf *top,
struct route_node *rn;
/* Sanity Check */
if ((top == NULL) && (top->new_table))
if (top == NULL)
return NULL;
if (IS_DEBUG_OSPF_SR)
@ -499,6 +491,12 @@ static int compute_link_nhlfe(struct sr_link *srl)
srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex;
srl->nhlfe[1].ifindex = nh->oi->ifp->ifindex;
/* Update neighbor address for LAN_ADJ_SID */
if (srl->type == LAN_ADJ_SID) {
IPV4_ADDR_COPY(&srl->nhlfe[0].nexthop, &nh->src);
IPV4_ADDR_COPY(&srl->nhlfe[1].nexthop, &nh->src);
}
/* Set Input & Output Label */
if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
srl->nhlfe[0].label_in = srl->sid[0];

View File

@ -49,11 +49,10 @@
/* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */
/* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */
#define SID_BASE_SIZE 4
#define SID_LABEL 3
#define SID_LABEL_SIZE (SID_BASE_SIZE + SID_LABEL)
#define SID_LABEL_SIZE(U) (U - 1)
#define SID_INDEX 4
#define SID_INDEX_SIZE (SID_BASE_SIZE + SID_INDEX)
#define SID_INDEX_SIZE(U) (U)
/* SID/Label Sub TLV - section 2.1 */
#define SUBTLV_SID_LABEL 1

View File

@ -3693,7 +3693,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
{
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
json_object *json_vrf = NULL, *json_intf_array = NULL;
json_object *json_vrf = NULL;
json_object *json_interface_sub = NULL, *json_interface = NULL;
if (use_json) {
@ -3701,7 +3701,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
json_vrf = json_object_new_object();
else
json_vrf = json;
json_intf_array = json_object_new_array();
json_interface = json_object_new_object();
}
if (ospf->instance) {
@ -3715,15 +3715,10 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (intf_name == NULL) {
if (use_json)
json_object_object_add(json_vrf, "interfaces",
json_intf_array);
/* Show All Interfaces.*/
FOR_ALL_INTERFACES (vrf, ifp) {
if (ospf_oi_count(ifp)) {
if (use_json) {
json_interface =
json_object_new_object();
json_interface_sub =
json_object_new_object();
}
@ -3732,14 +3727,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
use_json);
if (use_json) {
json_object_array_add(json_intf_array,
json_interface);
json_object_object_add(
json_interface, ifp->name,
json_interface_sub);
}
}
}
if (use_json)
json_object_object_add(json_vrf, "interfaces",
json_interface);
} else {
/* Interface name is specified. */
ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
@ -3753,19 +3749,17 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
if (use_json) {
json_interface_sub = json_object_new_object();
json_interface = json_object_new_object();
json_object_object_add(json_vrf, "interfaces",
json_intf_array);
}
show_ip_ospf_interface_sub(
vty, ospf, ifp, json_interface_sub, use_json);
if (use_json) {
json_object_array_add(json_intf_array,
json_interface);
json_object_object_add(json_interface,
ifp->name,
json_interface_sub);
json_object_object_add(json_vrf, "interfaces",
json_interface);
}
}
}
@ -4300,7 +4294,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
{
struct ospf_interface *oi;
struct listnode *node;
json_object *json_vrf = NULL, *json_nbr_array = NULL;
json_object *json_vrf = NULL;
json_object *json_nbr_sub = NULL;
if (use_json) {
@ -4308,7 +4302,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
json_vrf = json_object_new_object();
else
json_vrf = json;
json_nbr_array = json_object_new_array();
json_nbr_sub = json_object_new_object();
}
if (ospf->instance) {
@ -4322,21 +4316,16 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (!use_json)
show_ip_ospf_neighbour_header(vty);
else
json_object_object_add(json_vrf, "neighbors",
json_nbr_array);
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
if (ospf_interface_neighbor_count(oi) == 0)
continue;
if (use_json) {
json_nbr_sub = json_object_new_object();
json_object_array_add(json_nbr_array, json_nbr_sub);
}
show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json);
}
if (use_json) {
json_object_object_add(json_vrf, "neighbors",
json_nbr_sub);
if (use_vrf) {
if (ospf->vrf_id == VRF_DEFAULT)
json_object_object_add(json, "default",
@ -8558,6 +8547,10 @@ DEFUN (ospf_distance_ospf,
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx = 0;
ospf->distance_intra = 0;
ospf->distance_inter = 0;
ospf->distance_external = 0;
if (argv_find(argv, argc, "intra-area", &idx))
ospf->distance_intra = atoi(argv[idx + 1]->arg);
idx = 0;

View File

@ -308,12 +308,6 @@ static struct ospf *ospf_new(u_short instance, const char *name)
new->lsa_refresh_interval, &new->t_lsa_refresher);
new->lsa_refresher_started = monotime(NULL);
if ((ospf_sock_init(new)) < 0) {
zlog_err(
"ospf_new: fatal error: ospf_sock_init was unable to open "
"a socket");
exit(1);
}
if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) {
zlog_err(
"ospf_new: fatal error: stream_new(%u) failed allocating ibuf",
@ -321,7 +315,6 @@ static struct ospf *ospf_new(u_short instance, const char *name)
exit(1);
}
new->t_read = NULL;
thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
new->oi_write_q = list_new();
new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT;
@ -332,6 +325,16 @@ static struct ospf *ospf_new(u_short instance, const char *name)
QOBJ_REG(new, ospf);
new->fd = -1;
if ((ospf_sock_init(new)) < 0) {
if (new->vrf_id != VRF_UNKNOWN)
zlog_warn(
"%s: ospf_sock_init is unable to open a socket",
__func__);
return new;
}
thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
return new;
}
@ -2049,7 +2052,8 @@ static int ospf_vrf_delete(struct vrf *vrf)
static int ospf_vrf_enable(struct vrf *vrf)
{
struct ospf *ospf = NULL;
vrf_id_t old_vrf_id = VRF_DEFAULT;
vrf_id_t old_vrf_id;
int ret = 0;
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: VRF %s id %u enabled",
@ -2070,13 +2074,15 @@ static int ospf_vrf_enable(struct vrf *vrf)
zlog_err("ospf_sock_init: could not raise privs, %s",
safe_strerror(errno));
}
if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0)
return 0;
ret = ospf_sock_init(ospf);
if (ospfd_privs.change(ZPRIVS_LOWER)) {
zlog_err("ospf_sock_init: could not lower privs, %s",
safe_strerror(errno));
}
if (ret < 0 || ospf->fd <= 0)
return 0;
thread_add_read(master, ospf_read, ospf,
ospf->fd, &ospf->t_read);
ospf->oi_running = 1;
ospf_zebra_vrf_register(ospf);
ospf_router_id_update(ospf);
@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf)
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: ospf old_vrf_id %d unlinked",
__PRETTY_FUNCTION__, old_vrf_id);
thread_cancel(ospf->t_read);
close(ospf->fd);
ospf->fd = -1;
}
/* Note: This is a callback, the VRF will be deleted by the caller. */

1
pimd/.gitignore vendored
View File

@ -2,6 +2,7 @@
Makefile.in
libpim.a
pimd
mtracebis
test_igmpv3_join
tags
TAGS

View File

@ -173,4 +173,10 @@ C19 Provision to prevent group mode clash
flow. There could be some provision to prevent such a behavior,
but currently there is none.
C20 Multicast traceroute module is based on:
draft-ietf-idmr-traceroute-ipm-07
It only implements, so far, weak traceroutes. The multicast routing
state of the router is not quieried but RPF path is followed along
PIM and IGMP enabled interfaces.
-x-

View File

@ -59,6 +59,7 @@ debug commands:
clear ip pim interfaces Reset PIM interfaces
clear ip pim oil Rescan PIM OIL (output interface list)
debug igmp IGMP protocol activity
debug mtrace Mtrace protocol activity
debug mroute PIM interaction with kernel MFC cache
debug pim PIM protocol activity
debug pim zebra ZEBRA protocol activity
@ -76,4 +77,6 @@ statistics commands:
pimd:
show memory pim PIM memory statistics
vtysh:
mtrace Multicast traceroute
-x-

372
pimd/TODO
View File

@ -1,375 +1,21 @@
T1 DONE Implement debug command
test pim receive join
T2 DONE Implement debug command
test pim receive prune
T3 DONE Per-interface Downstream (S,G) state machine
(RFC 4601 4.5.3. Receiving (S,G) Join/Prune Messages)
T4 DONE Upstream (S,G) state machine
(RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages)
T5 DONE Verify Data Packet Forwarding Rules
RFC 4601 4.2. Data Packet Forwarding Rules
RFC 4601 4.8.2. PIM-SSM-Only Routers
Additionally, the Packet forwarding rules of Section 4.2 can be
simplified in a PIM-SSM-only router:
iif is the incoming interface of the packet.
oiflist = NULL
if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
oiflist = inherited_olist(S,G)
} else if (iif is in inherited_olist(S,G)) {
send Assert(S,G) on iif
}
oiflist = oiflist (-) iif
forward packet on all interfaces in oiflist
Macro:
inherited_olist(S,G) =
joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
T6 DONE Implement (S,G) Assert state machine (RFC 4601, section 4.6.1).
Changes in pim_ifchannel.ifassert_winner should trigger
pim_upstream_update_join_desired().
Depends on TODO T27.
Depends on TODO T33.
See also CAVEAT C7.
See also: RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
Transitions from Joined State
RPF'(S,G) changes due to an Assert
http://www.hep.ucl.ac.uk/~ytl/multi-cast/pim-dm_01.html:
The PIM Assert mechanism is used to shutoff duplicate flows onto
the same multiaccess network. Routers detect this condiction when
they receive an (S,G) packet via a multi-access interface that is
in the (S,G) OIL. This causes the routers to send Assert
Messages.
Note that neighbors will not accept Join/Prune or Assert messages
from a router unless they have first heard a Hello message from that
router. Thus, if a router needs to send a Join/Prune or Assert
message on an interface on which it has not yet sent a Hello message
with the currently configured IP address, then it MUST immediately
send the relevant Hello message without waiting for the Hello Timer
to expire, followed by the Join/Prune or Assert message.
T7 DONE Implement hello option: LAN Prune Delay
T8 DONE Implement J/P_Override_Interval(I)
Depends on TODO T7.
See pim_ifchannel.c, pim_ifchannel_prune(), jp_override_interval.
T9 DONE Detect change in IGMPv3 RPF interface/next-hop for S and update.
channel_oil vif index accordingly ?
Beware accidentaly adding looped MFC entries (IIF=OIF).
T10 DONE React to (S,G) join directed to another upstream address. See
also:
RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
If a router wishes to propagate a Join(S,G) upstream, it must also
watch for messages on its upstream interface from other routers on
that subnet, and these may modify its behavior. If it sees a
Join(S,G) to the correct upstream neighbor, it should suppress its
own Join(S,G). If it sees a Prune(S,G), Prune(S,G,rpt), or
Prune(*,G) to the correct upstream neighbor towards S, it should
be prepared to override that prune by scheduling a Join(S,G) to be
sent almost immediately.
T11 DONE Review protocol modifications for SSM
(RFC 4601 4.8.1. Protocol Modifications for SSM Destination
Addresses)
T12 DONE Review updates of RPF entries.
FIXME pim_upstream.c send_join():
Currently only one upstream state is affected by detection of RPF change.
RPF change should affect all upstream states sharing the RPF cache.
T13 DONE Check that RFC macros using S,G,RPF_interface(S) are actually
implemented with this strategy:
rpf_ifch=find_ifch(up->rpf->interface).
See pim_rpf.c pim_rpf_find_rpf_addr() for a correct example.
$ grep -i macro pimd/*.c
pimd/pim_iface.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros
pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros
pimd/pim_ifchannel.c: Macro:
pimd/pim_rpf.c: RFC 4601: 4.1.6. State Summarization Macros
T14 DONE Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
See pim_mroute.c mroute_msg().
T15 DONE Interface command to statically join (S,G).
interface eth0
ip igmp join-group 239.1.1.1 source 1.1.1.1
T16 DONE RPF'(S,G) lookup is not working for S reachable with default route.
See "RPF'(S,G) not found" in pim_rpf_update() from pim_rpf.c.
Zebra daemon RIB is not reflecting changes in kernel routes
accurately?
T17 DONE Prevent CLI from creating bogus interfaces.
Example:
conf t
interface xxx
T18 Consider reliable pim solution (refresh reduction)
T1 Consider reliable pim solution (refresh reduction)
A Reliable Transport Mechanism for PIM
http://tools.ietf.org/wg/pim/draft-ietf-pim-port/
PORT=PIM-Over-Reliable-Transport
T19 DONE Fix self as neighbor
See mailing list post:
http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
T20 DONE Fix debug message: "pim_neighbor_update: internal error:
trying to replace same prefix list"
See mailing list post:
http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
T21 DONE Clean-up PIM/IGMP interface mismatch debugging
See option PIM_CHECK_RECV_IFINDEX_SANITY in pimd/Makefile.am
See mailing list post:
http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00003.html
T22 DONE IGMP must be protected against adding looped MFC entries
created by both source and receiver attached to the same
interface.
T23 DONE libfrr crash after zclient_lookup_nexthop.
See mailing list post:
http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00008.html
T24 DONE zserv may return recursive routes:
- nexthop type is set to ZEBRA_NEXTHOP_IPV4
- ifindex is not reported
- calls expecting ifindex (fib_lookup_if_vif_index) are disrupted
See also this mailing list post:
[PATCH 21/21] Link detect and recursive routes
http://www.gossamer-threads.com/lists/quagga/dev/17564
T25 DONE Zclient nexthop lookup missing OSPF route to 1.1.1.1/32
See also:
pim_zlookup.c zclient_lookup_nexthop misses OSPF 1.1.1.1/32
zebra/zebra_vty.c show_ip_route_addr_cmd hits OSPF 1.1.1.1/32
T26 DONE Zebra daemon is marking recursive static route as inactive.
FIXED: zebra daemon was incorrectly marking recursive routes
pointing to kernel routes as inactive:
zebra/zebra_rib.c nexthop_active_ipv4:
-- Original:
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
-- Fixed:
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) ||
match->type == ZEBRA_ROUTE_KERNEL)
Old problem description:
This prevents rib_match_ipv4 from returning its nexthop:
client: pim_zlookup.c zclient_read_nexthop
server: zebra/zserv.c zsend_ipv4_nexthop_lookup_v2 -> rib_match_ipv4
Kernel route is injected into zebra in zebra_rib.c rib_add_ipv4
Examples:
rt_netlink.c:726: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0);
rt_netlink.c:864: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0);
This patch didn't fix the issue:
[PATCH 21/21] Link detect and recursive routes
http://www.gossamer-threads.com/lists/quagga/dev/17564
See the example below for the route 2.2.2.2.
bash# route add -host 1.1.1.1 gw 127.0.0.1
bash# route add -host 2.2.2.2 gw 1.1.1.1
bash# netstat -nvr
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
2.2.2.2 1.1.1.1 255.255.255.255 UGH 0 0 0 lo
1.1.1.1 127.0.0.1 255.255.255.255 UGH 0 0 0 lo
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
0.0.0.0 192.168.0.2 0.0.0.0 UG 0 0 0 eth0
bash#
zebra# sh ip route
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
I - ISIS, B - BGP, > - selected route, * - FIB route
K>* 0.0.0.0/0 via 192.168.0.2, eth0
K>* 1.1.1.1/32 via 127.0.0.1, lo
K * 2.2.2.2/32 via 1.1.1.1, lo inactive
C>* 127.0.0.0/8 is directly connected, lo
C>* 192.168.0.0/24 is directly connected, eth0
quagga-pimd-router# sh ip route 1.1.1.1
Address NextHop Interface Metric Preference
1.1.1.1 127.0.0.1 lo 0 0
quagga-pimd-router#
quagga-pimd-router# sh ip route 2.2.2.2
Address NextHop Interface Metric Preference
2.2.2.2 192.168.0.2 eth0 0 0
quagga-pimd-router#
T27 DONE Implement debug command
test pim receive assert
See also TODO T6: (S,G) Assert state machine.
T28 DONE Bad IPv4 address family=02 in Join/Prune dump
Reported by Andrew Lunn <andrew.lunn@ascom.ch>
# 58-byte pim v2 Join/Prune dump
# ------------------------------
# IPv4 address family=02 is wrong, correct IPv4 address family is 01
# See http://www.iana.org/assignments/address-family-numbers
#
c8XX YY03 : ip src 200.xx.yy.3
e000 000d : ip dst 224.0.0.13
9404 0000 : ip router alert option 148.4.0.0
2300 ab13 : pimv2,type=3 res=00 checksum=ab13
0200 : upstream family=02, encoding=00
c8XX YY08 : upstream 200.xx.yy.8
0001 00d2 : res=00 groups=01 holdtime=00d2
0200 0020 : group family=02, encoding=00, res=00, mask_len=20
ef01 0101 : group address 239.1.1.1
0001 0000 : joined=0001 pruned=0000
0200 0020 : source family=02, encoding=00, res=00, mask_len=20
0101 0101 : source address 1.1.1.1
T29 DONE Reset interface PIM-hello-sent counter when primary address changes
See pim_ifp->pim_ifstat_hello_sent
RFC 4601: 4.3.1. Sending Hello Messages
Thus, if a router needs to send a Join/Prune or Assert message on
an interface on which it has not yet sent a Hello message with the
currently configured IP address, then it MUST immediately send the
relevant Hello message without waiting for the Hello Timer to
expire, followed by the Join/Prune or Assert message.
T30 DONE Run interface DR election when primary address changes
Reported by Andrew Lunn <andrew.lunn@ascom.ch>
See pim_if_dr_election().
T31 If an interface changes one of its secondary IP addresses, a Hello
T2 If an interface changes one of its secondary IP addresses, a Hello
message with an updated Address_List option and a non-zero
HoldTime should be sent immediately.
See also detect_secondary_address_change
See also CAVEAT C15.
See also RFC 4601: 4.3.1. Sending Hello Messages
T32 FIXED Detection of interface primary address changes may fail when
there are multiple addresses.
See also CAVEAT C14.
pim_find_primary_addr() should return interface primary address
from connected list. Currently it returns the first address.
Zebra daemon "show int" is able to keep the primary address as
first address.
T33 DONE Implement debug command: test pim receive upcall
See also TODO T6: (S,G) Assert state machine.
T34 DONE assert_action_a1
T35 DONE Review macros depending on interface I.
See also: grep ,I\) pimd/*.c
For the case (S,G,I) check if I is either
1) interface attached to this per-interface S,G state (don't think so)
or
2) an arbitrary interface (most probably)
For the arbitrary interface case (2), consider representing
interface ifp as its primary address (struct in_addr ifaddr). The
benefit is in_addr does not need to be dereferenced, so it does
not demand protection against crashes.
T36 DONE React to zebra daemon link-detect up/down notification.
pim_ifp->primary_address is managed by detect_primary_address_change()
depending on to ifp->connected (managed by zebra_interface_address_read()).
T37 DONE Review list of variables which may affect pim_upstream.c
pim_upstream_evaluate_join_desired().
Call pim_upstream_update_join_desired() accordingly.
See the order of invokation:
pim_if_dr_election(ifp);
pim_if_update_join_desired(pim_ifp); /* depends on DR */
pim_if_update_could_assert(ifp); /* depends on DR */
pim_if_update_my_assert_metric(ifp); /* depends on could_assert */
join_desired depends on:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->ifassert_winner_metric
ch->ifassert_winner
ch->local_ifmembership
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.mrib_metric_preference
ch->upstream->rpf.source_nexthop.mrib_route_metric
ch->upstream->rpf.source_nexthop.interface
T38 DONE Detect change in AssertTrackingDesired(S,G,I)
See the order of invokation:
dr_election: none
update_join_desired: depends on DR
update_tracking_desired: depends on DR, join_desired
AssertTrackingDesired(S,G,I) depends on:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->local_ifmembership
ch->ifassert_winner
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.interface
PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags)
T39 DONE AssertTrackingDesired: flags is not matching evaluation
# show ip pim assert-internal
CA: CouldAssert
ECA: Evaluate CouldAssert
ATD: AssertTrackingDesired
eATD: Evaluate AssertTrackingDesired
Interface Address Source Group CA eCA ATD eATD
eth0 192.168.1.100 1.1.1.1 239.1.1.1 no no no yes
#
T40 Lightweight MLDv2
T3 Lightweight MLDv2
http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05
http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
http://www.ietf.org/html.charters/mboned-charter.html
T41 DONE ssmping support
See also:
http://www.venaas.no/multicast/ssmping/
draft-ietf-mboned-ssmping-07
http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07
Example:
debug ssmpingd
conf t
ip ssmpingd 1.1.1.1
show ip ssmpingd
T42 Static igmp join fails when loading config at boot time
T4 Static igmp join fails when loading config at boot time
! Wrong behavior seen at boot time:
!
@ -396,7 +42,7 @@ T42 Static igmp join fails when loading config at boot time
eth0 200.202.112.3 2 2 0 0 0 0
lo 127.0.0.1 1 1 0 0 0 0
T43 PIM Neighbor Reduction
T5 PIM Neighbor Reduction
https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/
"In a transit LAN (no directly connected source or receiver), many
@ -404,19 +50,19 @@ T43 PIM Neighbor Reduction
a procedure to reduce the amount of neighbors established over a
transit LAN."
T44 Single Stream Multicast Fast Reroute (SMFR) Method
T6 Single Stream Multicast Fast Reroute (SMFR) Method
https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/
"This document proposes an IP multicast fast convergence method
based on differentiating primary and backup PIM join."
T45 RFC5384 - The Join Attribute Format
T7 RFC5384 - The Join Attribute Format
"This document describes a modification of the Join message that
allows a node to associate attributes with a particular tree."
T46 PIM Multi-Topology ID (MT-ID) Join-Attribute
T8 PIM Multi-Topology ID (MT-ID) Join-Attribute
http://tools.ietf.org/html/draft-cai-pim-mtid-00
Depends on T45.
Depends on T7.
"This draft introduces a new type of PIM Join Attribute used to
encode the identity of the topology PIM uses for RPF."

373
pimd/mtracebis.c Normal file
View File

@ -0,0 +1,373 @@
/*
* Multicast Traceroute for FRRouting
* Copyright (C) 2018 Mladen Sablic
*
* 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
*/
#ifdef __linux__
#include "pim_igmp_mtrace.h"
#include "checksum.h"
#include "mtracebis_routeget.h"
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <net/if.h>
#include <unistd.h>
#include <getopt.h>
#define MTRACEBIS_VERSION "0.1"
#define MTRACE_TIMEOUT (5)
#define IP_HDR_LEN (sizeof(struct ip))
#define IP_RA_LEN (4)
#define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
#define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
static const char *progname;
static void usage(void)
{
fprintf(stderr, "Usage : %s <multicast source>\n", progname);
}
static void version(void)
{
fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION);
}
static int send_query(int fd, struct in_addr to_addr,
struct igmp_mtrace *mtrace)
{
struct sockaddr_in to;
socklen_t tolen;
int sent;
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_addr = to_addr;
tolen = sizeof(to);
sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent < 1)
return -1;
return 0;
}
static void print_query(struct igmp_mtrace *mtrace)
{
char src_str[INET_ADDRSTRLEN];
char dst_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
printf("* Mtrace from %s to %s via group %s\n",
inet_ntop(AF_INET, &mtrace->src_addr, src_str, sizeof(src_str)),
inet_ntop(AF_INET, &mtrace->dst_addr, dst_str, sizeof(dst_str)),
inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str)));
}
static int recv_response(int fd, long msec, int *hops)
{
int recvd;
char mtrace_buf[IP_AND_MTRACE_BUF_LEN];
struct ip *ip;
struct igmp_mtrace *mtrace;
int mtrace_len;
int responses;
int i;
u_short sum;
recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0);
if (recvd < 1) {
fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
return -1;
}
if (recvd < (int)sizeof(struct ip)) {
fprintf(stderr, "no ip header\n");
return -1;
}
ip = (struct ip *)mtrace_buf;
if (ip->ip_v != 4) {
fprintf(stderr, "IP not version 4\n");
return -1;
}
sum = ip->ip_sum;
ip->ip_sum = 0;
if (sum != in_cksum(ip, ip->ip_hl * 4))
return -1;
mtrace = (struct igmp_mtrace *)(mtrace_buf + (4 * ip->ip_hl));
mtrace_len = ntohs(ip->ip_len) - ip->ip_hl * 4;
if (mtrace_len < (int)MTRACE_HDR_SIZE)
return -1;
sum = mtrace->checksum;
mtrace->checksum = 0;
if (sum != in_cksum(mtrace, mtrace_len)) {
fprintf(stderr, "mtrace checksum wrong\n");
return -1;
}
if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE)
return -1;
responses = mtrace_len - sizeof(struct igmp_mtrace);
responses /= sizeof(struct igmp_mtrace_rsp);
printf("%ld ms received responses from %d hops.\n", msec, responses);
if (hops)
*hops = responses;
for (i = 0; i < responses; i++) {
struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i];
if (rsp->fwd_code != 0)
printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code);
}
return 0;
}
static int wait_for_response(int fd, int *hops)
{
fd_set readfds;
struct timeval timeout;
int ret = -1;
long msec, rmsec, tmsec;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
memset(&timeout, 0, sizeof(timeout));
timeout.tv_sec = MTRACE_TIMEOUT;
tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
do {
ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
if (ret <= 0)
return ret;
rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
msec = tmsec - rmsec;
} while (recv_response(fd, msec, hops) != 0);
return ret;
}
int main(int argc, char *const argv[])
{
struct in_addr mc_source;
struct in_addr iface_addr;
struct in_addr gw_addr;
struct in_addr mtrace_addr;
struct igmp_mtrace mtrace;
int hops = 255;
int rhops;
int maxhops = 255;
int perhop = 3;
int ifindex;
int unicast = 1;
int ttl = 64;
int fd = -1;
int ret = -1;
int c;
int i, j;
char ifname[IF_NAMESIZE];
char ip_str[INET_ADDRSTRLEN];
mtrace_addr.s_addr = inet_addr("224.0.1.32");
uid_t uid = getuid();
if (uid != 0) {
printf("must run as root\n");
exit(EXIT_FAILURE);
}
if (argc <= 0)
progname = "mtracebis";
else
progname = argv[0];
if (argc != 2) {
usage();
exit(EXIT_FAILURE);
}
while (1) {
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0} };
int option_index = 0;
c = getopt_long(argc, argv, "vh", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
usage();
exit(0);
case 'v':
version();
exit(0);
default:
usage();
exit(EXIT_FAILURE);
}
}
if (inet_pton(AF_INET, argv[1], &mc_source) != 1) {
usage();
fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0],
argv[1]);
exit(EXIT_FAILURE);
}
ifindex = routeget(mc_source, &iface_addr, &gw_addr);
if (ifindex < 0) {
fprintf(stderr, "%s: failed to get route to source %s\n",
argv[0], argv[1]);
exit(EXIT_FAILURE);
}
if (if_indextoname(ifindex, ifname) == NULL) {
fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0],
strerror(errno));
exit(EXIT_FAILURE);
}
/* zero mtrace struct */
memset((char *)&mtrace, 0, sizeof(mtrace));
/* set up query */
mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST;
mtrace.hops = hops;
mtrace.checksum = 0;
mtrace.grp_addr.s_addr = 0;
mtrace.src_addr = mc_source;
mtrace.dst_addr = iface_addr;
mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr;
mtrace.rsp_ttl = ttl;
mtrace.qry_id = 0xffffff & time(NULL);
mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
if (fd < 1) {
fprintf(stderr, "%s: socket error: %s\n", argv[0],
strerror(errno));
exit(EXIT_FAILURE);
}
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
strlen(ifname));
if (ret < 0) {
fprintf(stderr, "%s: setsockopt error: %s\n", argv[0],
strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
print_query(&mtrace);
if (send_query(fd, gw_addr, &mtrace) < 0) {
fprintf(stderr, "%s: sendto error: %s\n", argv[0],
strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
printf("Querying full reverse path...\n");
ret = wait_for_response(fd, NULL);
if (ret > 0) {
ret = 0;
goto close_fd;
}
if (ret < 0) {
fprintf(stderr, "%s: select error: %s\n", argv[0],
strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
printf(" * ");
printf("switching to hop-by-hop:\n");
printf("%3d ? (%s)\n", 0,
inet_ntop(AF_INET, &mtrace.dst_addr, ip_str, sizeof(ip_str)));
for (i = 1; i < maxhops; i++) {
printf("%3d ", -i);
mtrace.hops = i;
for (j = 0; j < perhop; j++) {
mtrace.qry_id++;
mtrace.checksum = 0;
mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
if (send_query(fd, gw_addr, &mtrace) < 0) {
fprintf(stderr, "%s: sendto error: %s\n",
argv[0], strerror(errno));
ret = EXIT_FAILURE;
goto close_fd;
}
ret = wait_for_response(fd, &rhops);
if (ret > 0) {
if (i > rhops) {
ret = 0;
goto close_fd;
}
break;
}
printf(" *");
}
if (ret <= 0)
printf("\n");
}
ret = 0;
close_fd:
close(fd);
exit(ret);
}
#else /* __linux__ */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf("%s implemented only for GNU/Linux\n", argv[0]);
exit(0);
}
#endif /* __linux__ */

700
pimd/mtracebis_netlink.c Normal file
View File

@ -0,0 +1,700 @@
/*
* libnetlink.c RTnetlink service routines.
*
* 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.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#ifdef __linux__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/uio.h>
#include <assert.h>
#include "mtracebis_netlink.h"
int rcvbuf = 1024 * 1024;
void rtnl_close(struct rtnl_handle *rth)
{
if (rth->fd >= 0) {
close(rth->fd);
rth->fd = -1;
}
}
int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
int protocol)
{
socklen_t addr_len;
int sndbuf = 32768;
memset(rth, 0, sizeof(*rth));
rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
if (rth->fd < 0) {
perror("Cannot open netlink socket");
return -1;
}
if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
perror("SO_SNDBUF");
return -1;
}
if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
perror("SO_RCVBUF");
return -1;
}
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
rth->local.nl_groups = subscriptions;
if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
perror("Cannot bind netlink socket");
return -1;
}
addr_len = sizeof(rth->local);
if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
perror("Cannot getsockname");
return -1;
}
if (addr_len != sizeof(rth->local)) {
fprintf(stderr, "Wrong address length %d\n", addr_len);
return -1;
}
if (rth->local.nl_family != AF_NETLINK) {
fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
return -1;
}
rth->seq = time(NULL);
return 0;
}
int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
{
return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
}
int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
{
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = type;
req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
req.g.rtgen_family = family;
return send(rth->fd, (void*)&req, sizeof(req), 0);
}
int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
{
return send(rth->fd, buf, len, 0);
}
int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len)
{
struct nlmsghdr *h;
int status;
char resp[1024];
status = send(rth->fd, buf, len, 0);
if (status < 0)
return status;
/* Check for immediate errors */
status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
if (status < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}
for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, (uint32_t)status);
h = NLMSG_NEXT(h, status)) {
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
fprintf(stderr, "ERROR truncated\n");
else
errno = -err->error;
return -1;
}
}
return 0;
}
int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
{
struct nlmsghdr nlh;
struct sockaddr_nl nladdr;
struct iovec iov[2] = {
{ .iov_base = &nlh, .iov_len = sizeof(nlh) },
{ .iov_base = req, .iov_len = len }
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = iov,
.msg_iovlen = 2,
};
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nlh.nlmsg_len = NLMSG_LENGTH(len);
nlh.nlmsg_type = type;
nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
nlh.nlmsg_pid = 0;
nlh.nlmsg_seq = rth->dump = ++rth->seq;
return sendmsg(rth->fd, &msg, 0);
}
int rtnl_dump_filter_l(struct rtnl_handle *rth,
const struct rtnl_dump_filter_arg *arg)
{
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[16384];
iov.iov_base = buf;
while (1) {
int status;
const struct rtnl_dump_filter_arg *a;
int found_done = 0;
int msglen = 0;
iov.iov_len = sizeof(buf);
status = recvmsg(rth->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)\n",
strerror(errno), errno);
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink\n");
return -1;
}
for (a = arg; a->filter; a++) {
struct nlmsghdr *h = (struct nlmsghdr*)buf;
msglen = status;
while (NLMSG_OK(h, (uint32_t)msglen)) {
int err;
if (nladdr.nl_pid != 0 ||
h->nlmsg_pid != rth->local.nl_pid ||
h->nlmsg_seq != rth->dump) {
if (a->junk) {
err = a->junk(&nladdr, h,
a->arg2);
if (err < 0)
return err;
}
goto skip_it;
}
if (h->nlmsg_type == NLMSG_DONE) {
found_done = 1;
break; /* process next filter */
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
fprintf(stderr,
"ERROR truncated\n");
} else {
errno = -err->error;
perror("RTNETLINK answers");
}
return -1;
}
err = a->filter(&nladdr, h, a->arg1);
if (err < 0)
return err;
skip_it:
h = NLMSG_NEXT(h, msglen);
}
}
if (found_done)
return 0;
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated\n");
continue;
}
if (msglen) {
fprintf(stderr, "!!!Remnant of size %d\n", msglen);
exit(1);
}
}
}
int rtnl_dump_filter(struct rtnl_handle *rth,
rtnl_filter_t filter,
void *arg1,
rtnl_filter_t junk,
void *arg2)
{
const struct rtnl_dump_filter_arg a[2] = {
{ .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
{ .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL }
};
return rtnl_dump_filter_l(rth, a);
}
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
unsigned groups, struct nlmsghdr *answer,
rtnl_filter_t junk,
void *jarg)
{
int status;
unsigned seq;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
struct iovec iov = {
.iov_base = (void*) n,
.iov_len = n->nlmsg_len
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[16384];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = peer;
nladdr.nl_groups = groups;
n->nlmsg_seq = seq = ++rtnl->seq;
if (answer == NULL)
n->nlmsg_flags |= NLM_F_ACK;
status = sendmsg(rtnl->fd, &msg, 0);
if (status < 0) {
perror("Cannot talk to rtnetlink");
return -1;
}
memset(buf,0,sizeof(buf));
iov.iov_base = buf;
while (1) {
iov.iov_len = sizeof(buf);
status = recvmsg(rtnl->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)\n",
strerror(errno), errno);
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink\n");
return -1;
}
if (msg.msg_namelen != sizeof(nladdr)) {
fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
exit(1);
}
for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
int err;
int len = h->nlmsg_len;
int l = len - sizeof(*h);
if (l<0 || len>status) {
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Truncated message\n");
return -1;
}
fprintf(stderr, "!!!malformed message: len=%d\n", len);
exit(1);
}
if ((int)nladdr.nl_pid != peer ||
h->nlmsg_pid != rtnl->local.nl_pid ||
h->nlmsg_seq != seq) {
if (junk) {
err = junk(&nladdr, h, jarg);
if (err < 0)
return err;
}
/* Don't forget to skip that message. */
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
continue;
}
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
if (l < (int)sizeof(struct nlmsgerr)) {
fprintf(stderr, "ERROR truncated\n");
} else {
errno = -err->error;
if (errno == 0) {
if (answer)
memcpy(answer, h, h->nlmsg_len);
return 0;
}
perror("RTNETLINK answers");
}
return -1;
}
if (answer) {
memcpy(answer, h, h->nlmsg_len);
return 0;
}
fprintf(stderr, "Unexpected reply!!!\n");
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated\n");
continue;
}
if (status) {
fprintf(stderr, "!!!Remnant of size %d\n", status);
exit(1);
}
}
}
int rtnl_listen(struct rtnl_handle *rtnl,
rtnl_filter_t handler,
void *jarg)
{
int status;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[8192];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
iov.iov_base = buf;
while (1) {
iov.iov_len = sizeof(buf);
status = recvmsg(rtnl->fd, &msg, 0);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
fprintf(stderr, "netlink receive error %s (%d)\n",
strerror(errno), errno);
if (errno == ENOBUFS)
continue;
return -1;
}
if (status == 0) {
fprintf(stderr, "EOF on netlink\n");
return -1;
}
if (msg.msg_namelen != sizeof(nladdr)) {
fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
exit(1);
}
for (h =(struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
int err;
int len = h->nlmsg_len;
int l = len - sizeof(*h);
if (l<0 || len>status) {
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Truncated message\n");
return -1;
}
fprintf(stderr, "!!!malformed message: len=%d\n", len);
exit(1);
}
err = handler(&nladdr, h, jarg);
if (err < 0)
return err;
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
fprintf(stderr, "Message truncated\n");
continue;
}
if (status) {
fprintf(stderr, "!!!Remnant of size %d\n", status);
exit(1);
}
}
}
int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
void *jarg)
{
int status;
struct sockaddr_nl nladdr;
char buf[8192];
struct nlmsghdr *h = (void*)buf;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
while (1) {
int err, len;
int l;
status = fread(&buf, 1, sizeof(*h), rtnl);
if (status < 0) {
if (errno == EINTR)
continue;
perror("rtnl_from_file: fread");
return -1;
}
if (status == 0)
return 0;
len = h->nlmsg_len;
l = len - sizeof(*h);
if (l<0 || len>(int)sizeof(buf)) {
fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
len, ftell(rtnl));
return -1;
}
status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
if (status < 0) {
perror("rtnl_from_file: fread");
return -1;
}
if (status < l) {
fprintf(stderr, "rtnl-from_file: truncated message\n");
return -1;
}
err = handler(&nladdr, h, jarg);
if (err < 0)
return err;
}
}
int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
{
int len = RTA_LENGTH(4);
struct rtattr *rta;
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), &data, 4);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
if (data)
memcpy(RTA_DATA(rta), data, alen);
else
assert(alen == 0);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
return 0;
}
int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
{
if ((int)(NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len)) > maxlen) {
fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
return -1;
}
memcpy(NLMSG_TAIL(n), data, len);
memset((uint8_t *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
return 0;
}
struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
{
struct rtattr *nest = NLMSG_TAIL(n);
addattr_l(n, maxlen, type, NULL, 0);
return nest;
}
int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
{
nest->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)nest;
return n->nlmsg_len;
}
struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
const void *data, int len)
{
struct rtattr *start = NLMSG_TAIL(n);
addattr_l(n, maxlen, type, data, len);
addattr_nest(n, maxlen, type);
return start;
}
int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
{
struct rtattr *nest = start + NLMSG_ALIGN(start->rta_len);
start->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)start;
addattr_nest_end(n, nest);
return n->nlmsg_len;
}
int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
{
int len = RTA_LENGTH(4);
struct rtattr *subrta;
if ((int)(RTA_ALIGN(rta->rta_len) + len) > maxlen) {
fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
return -1;
}
subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
subrta->rta_type = type;
subrta->rta_len = len;
memcpy(RTA_DATA(subrta), &data, 4);
rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
return 0;
}
int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
const void *data, int alen)
{
struct rtattr *subrta;
int len = RTA_LENGTH(alen);
if ((int)(RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen) {
fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
return -1;
}
subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
subrta->rta_type = type;
subrta->rta_len = len;
memcpy(RTA_DATA(subrta), data, alen);
rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
return 0;
}
int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
tb[rta->rta_type] = rta;
rta = RTA_NEXT(rta,len);
}
if (len)
fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
return 0;
}
int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
int i = 0;
memset(tb, 0, sizeof(struct rtattr *) * max);
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max && i < max)
tb[i++] = rta;
rta = RTA_NEXT(rta,len);
}
if (len)
fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
return i;
}
int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
int len)
{
if ((int)RTA_PAYLOAD(rta) < len)
return -1;
if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
rta = (struct rtattr *)(uint8_t *)RTA_DATA(rta)+RTA_ALIGN(len);
return parse_rtattr_nested(tb, max, rta);
}
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
return 0;
}
#endif /* __linux__ */

130
pimd/mtracebis_netlink.h Normal file
View File

@ -0,0 +1,130 @@
/*
* libnetlink.c RTnetlink service routines.
*
* 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.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#ifdef __linux__
#ifndef __LIBNETLINK_H__
#define __LIBNETLINK_H__ 1
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_link.h>
#include <linux/if_addr.h>
#include <linux/neighbour.h>
struct rtnl_handle
{
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
};
extern int rcvbuf;
extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
extern void rtnl_close(struct rtnl_handle *rth);
extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
struct nlmsghdr *n, void *);
struct rtnl_dump_filter_arg
{
rtnl_filter_t filter;
void *arg1;
rtnl_filter_t junk;
void *arg2;
};
extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
const struct rtnl_dump_filter_arg *arg);
extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
void *arg1,
rtnl_filter_t junk,
void *arg2);
extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
unsigned groups, struct nlmsghdr *answer,
rtnl_filter_t junk,
void *jarg);
extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
extern int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int);
extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
#define parse_rtattr_nested(tb, max, rta) \
(parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
__parse_rtattr_nested_compat(tb, max, rta, len); })
extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
void *jarg);
extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
void *jarg);
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((uint8_t *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
#ifndef IFA_RTA
#define IFA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
#endif
#ifndef IFA_PAYLOAD
#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
#endif
#ifndef IFLA_RTA
#define IFLA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
#endif
#ifndef IFLA_PAYLOAD
#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
#endif
#ifndef NDA_RTA
#define NDA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
#endif
#ifndef NDA_PAYLOAD
#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
#endif
#ifndef NDTA_RTA
#define NDTA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
#endif
#ifndef NDTA_PAYLOAD
#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
#endif
#endif /* __LIBNETLINK_H__ */
#endif /* __linux__ */

98
pimd/mtracebis_routeget.c Normal file
View File

@ -0,0 +1,98 @@
/*
* Multicast Traceroute for FRRouting
* Copyright (C) 2018 Mladen Sablic
*
* 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
*/
#ifdef __linux__
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/types.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include "mtracebis_netlink.h"
#include "mtracebis_routeget.h"
static int find_dst(struct nlmsghdr *n, struct in_addr *src, struct in_addr *gw)
{
struct rtmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr *tb[RTA_MAX + 1];
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
}
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
if (tb[RTA_PREFSRC])
src->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_PREFSRC]);
if (tb[RTA_GATEWAY])
gw->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_GATEWAY]);
if (tb[RTA_OIF])
return *(int *)RTA_DATA(tb[RTA_OIF]);
return 0;
}
int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw)
{
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[1024];
} req;
int ret;
struct rtnl_handle rth = {.fd = -1};
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST;
req.n.nlmsg_type = RTM_GETROUTE;
req.r.rtm_family = AF_INET;
req.r.rtm_table = 0;
req.r.rtm_protocol = 0;
req.r.rtm_scope = 0;
req.r.rtm_type = 0;
req.r.rtm_src_len = 0;
req.r.rtm_dst_len = 0;
req.r.rtm_tos = 0;
addattr_l(&req.n, sizeof(req), RTA_DST, &dst.s_addr, 4);
req.r.rtm_dst_len = 32;
ret = rtnl_open(&rth, 0);
if (ret < 0)
return ret;
if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
ret = -1;
goto close_rth;
}
ret = find_dst(&req.n, src, gw);
close_rth:
rtnl_close(&rth);
return ret;
}
#endif /* __linux__ */

31
pimd/mtracebis_routeget.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Multicast Traceroute for FRRouting
* Copyright (C) 2018 Mladen Sablic
*
* 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
*/
#ifdef __linux__
#ifndef ROUTEGET_H
#define ROUTEGET_H
#include <netinet/in.h>
int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw);
#endif /* ROUTEGET */
#endif /* __linux__ */

View File

@ -7311,6 +7311,27 @@ DEFUN (no_debug_msdp_packets,
ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
DEFUN (debug_mtrace,
debug_mtrace_cmd,
"debug mtrace",
DEBUG_STR
DEBUG_MTRACE_STR)
{
PIM_DO_DEBUG_MTRACE;
return CMD_SUCCESS;
}
DEFUN (no_debug_mtrace,
no_debug_mtrace_cmd,
"no debug mtrace",
NO_STR
DEBUG_STR
DEBUG_MTRACE_STR)
{
PIM_DONT_DEBUG_MTRACE;
return CMD_SUCCESS;
}
DEFUN_NOSH (show_debugging_pim,
show_debugging_pim_cmd,
"show debugging [pim]",
@ -8721,6 +8742,8 @@ void pim_cmd_init(void)
install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &undebug_msdp_packets_cmd);
install_element(ENABLE_NODE, &debug_mtrace_cmd);
install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &debug_igmp_cmd);
install_element(CONFIG_NODE, &no_debug_igmp_cmd);
@ -8763,6 +8786,8 @@ void pim_cmd_init(void)
install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &undebug_msdp_packets_cmd);
install_element(CONFIG_NODE, &debug_mtrace_cmd);
install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);

View File

@ -63,6 +63,7 @@
#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n"
#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n"
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
#define DEBUG_MTRACE_STR "Mtrace protocol activity\n"
void pim_cmd_init(void);

View File

@ -85,9 +85,11 @@ static void *if_list_clean(struct pim_interface *pim_ifp)
if (pim_ifp->sec_addr_list)
list_delete_and_null(&pim_ifp->sec_addr_list);
while ((ch = RB_ROOT(pim_ifchannel_rb,
&pim_ifp->ifchannel_rb)) != NULL)
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
pim_ifchannel_delete(ch);
}
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
@ -250,9 +252,11 @@ void pim_if_delete(struct interface *ifp)
if (pim_ifp->boundary_oil_plist)
XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
while ((ch = RB_ROOT(pim_ifchannel_rb,
&pim_ifp->ifchannel_rb)) != NULL)
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
pim_ifchannel_delete(ch);
}
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);

View File

@ -211,8 +211,9 @@ void pim_ifchannel_delete_all(struct interface *ifp)
if (!pim_ifp)
return;
while ((ch = RB_ROOT(pim_ifchannel_rb,
&pim_ifp->ifchannel_rb)) != NULL) {
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
pim_ifchannel_delete(ch);
}
}

View File

@ -29,6 +29,7 @@
#include "pim_igmp.h"
#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
#include "pim_igmp_mtrace.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_mroute.h"
@ -504,6 +505,16 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
case PIM_IGMP_V2_LEAVE_GROUP:
return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
igmp_msg, igmp_msg_len);
case PIM_IGMP_MTRACE_RESPONSE:
return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
from_str, igmp_msg,
igmp_msg_len);
break;
case PIM_IGMP_MTRACE_QUERY_REQUEST:
return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
from_str, igmp_msg,
igmp_msg_len);
}
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);

View File

@ -37,6 +37,8 @@
#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
#define PIM_IGMP_V2_LEAVE_GROUP (0x17)
#define PIM_IGMP_MTRACE_RESPONSE (0x1E)
#define PIM_IGMP_MTRACE_QUERY_REQUEST (0x1F)
#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
#define IGMP_V3_REPORT_HEADER_SIZE (8)

727
pimd/pim_igmp_mtrace.c Normal file
View File

@ -0,0 +1,727 @@
/*
* Multicast traceroute for FRRouting
* Copyright (C) 2017 Mladen Sablic
*
* 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 "pimd.h"
#include "pim_util.h"
#include "pim_sock.h"
#include "pim_rp.h"
#include "pim_oil.h"
#include "pim_ifchannel.h"
#include "pim_macro.h"
#include "pim_igmp_mtrace.h"
static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
{
mtrace_rspp->arrival = 0;
mtrace_rspp->incoming.s_addr = 0;
mtrace_rspp->outgoing.s_addr = 0;
mtrace_rspp->prev_hop.s_addr = 0;
mtrace_rspp->in_count = MTRACE_UNKNOWN_COUNT;
mtrace_rspp->out_count = MTRACE_UNKNOWN_COUNT;
mtrace_rspp->total = MTRACE_UNKNOWN_COUNT;
mtrace_rspp->rtg_proto = 0;
mtrace_rspp->fwd_ttl = 0;
mtrace_rspp->mbz = 0;
mtrace_rspp->s = 0;
mtrace_rspp->src_mask = 0;
mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR;
}
static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
struct igmp_mtrace_rsp *mrspp)
{
char inc_str[INET_ADDRSTRLEN];
char out_str[INET_ADDRSTRLEN];
char prv_str[INET_ADDRSTRLEN];
zlog_debug(
"Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
rsp, ntohl(qry_id), mrspp->arrival,
inet_ntop(AF_INET, &(mrspp->incoming), inc_str,
sizeof(inc_str)),
inet_ntop(AF_INET, &(mrspp->outgoing), out_str,
sizeof(out_str)),
inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str,
sizeof(prv_str)),
mrspp->rtg_proto, mrspp->fwd_code);
}
static void mtrace_debug(struct pim_interface *pim_ifp,
struct igmp_mtrace *mtracep, int mtrace_len)
{
char inc_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
char src_str[INET_ADDRSTRLEN];
char dst_str[INET_ADDRSTRLEN];
char rsp_str[INET_ADDRSTRLEN];
zlog_debug(
"Rx mtrace packet incoming on %s: "
"hops=%d type=%d size=%d, grp=%s, src=%s,"
" dst=%s rsp=%s ttl=%d qid=%ud",
inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
sizeof(inc_str)),
mtracep->hops, mtracep->type, mtrace_len,
inet_ntop(AF_INET, &(mtracep->grp_addr), grp_str,
sizeof(grp_str)),
inet_ntop(AF_INET, &(mtracep->src_addr), src_str,
sizeof(src_str)),
inet_ntop(AF_INET, &(mtracep->dst_addr), dst_str,
sizeof(dst_str)),
inet_ntop(AF_INET, &(mtracep->rsp_addr), rsp_str,
sizeof(rsp_str)),
mtracep->rsp_ttl, ntohl(mtracep->qry_id));
if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
int i;
int responses = mtrace_len - sizeof(struct igmp_mtrace);
if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Mtrace response block of wrong"
" length");
responses = responses / sizeof(struct igmp_mtrace_rsp);
for (i = 0; i < responses; i++)
mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]);
}
}
/* 5.1 Query Arrival Time */
static uint32_t query_arrival_time(void)
{
struct timeval tv;
uint32_t qat;
char m_qat[] = "Query arrival time lookup failed: errno=%d: %s";
if (gettimeofday(&tv, NULL) < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(m_qat, errno, safe_strerror(errno));
return 0;
}
/* not sure second offset correct, as I get different value */
qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625);
return qat;
}
static int mtrace_send_packet(struct interface *ifp,
struct igmp_mtrace *mtracep,
size_t mtrace_buf_len, struct in_addr dst_addr,
struct in_addr group_addr)
{
struct sockaddr_in to;
struct pim_interface *pim_ifp;
socklen_t tolen;
ssize_t sent;
int ret;
int fd;
char pim_str[INET_ADDRSTRLEN];
char rsp_str[INET_ADDRSTRLEN];
u_char ttl;
pim_ifp = ifp->info;
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_addr = dst_addr;
tolen = sizeof(to);
if (PIM_DEBUG_MTRACE)
zlog_debug("Sending mtrace packet to %s on %s",
inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str,
sizeof(rsp_str)),
inet_ntop(AF_INET, &pim_ifp->primary_address,
pim_str, sizeof(pim_str)));
fd = pim_socket_raw(IPPROTO_IGMP);
if (fd < 0)
return -1;
ret = pim_socket_bind(fd, ifp);
if (ret < 0) {
ret = -1;
goto close_fd;
}
if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) {
if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) {
ttl = 1;
} else {
if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE)
ttl = mtracep->rsp_ttl;
else
ttl = 64;
}
ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof(ttl));
if (ret < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn("Failed to set socket multicast TTL");
ret = -1;
goto close_fd;
}
}
sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent != (ssize_t)mtrace_buf_len) {
char dst_str[INET_ADDRSTRLEN];
char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str,
sizeof(group_str));
if (sent < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Send mtrace request failed for %s on"
"%s: group=%s msg_size=%zd: errno=%d: "
" %s",
dst_str, ifp->name, group_str,
mtrace_buf_len, errno,
safe_strerror(errno));
} else {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Send mtrace request failed for %s on"
" %s: group=%s msg_size=%zd: sent=%zd",
dst_str, ifp->name, group_str,
mtrace_buf_len, sent);
}
ret = -1;
goto close_fd;
}
ret = 0;
close_fd:
close(fd);
return ret;
}
static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
struct interface *interface)
{
struct pim_nexthop nexthop;
struct sockaddr_in to;
struct interface *if_out;
socklen_t tolen;
int ret;
int fd;
int sent;
uint16_t checksum;
checksum = ip_hdr->ip_sum;
ip_hdr->ip_sum = 0;
if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4))
return -1;
if (ip_hdr->ip_ttl-- <= 1)
return -1;
ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4);
fd = pim_socket_raw(IPPROTO_RAW);
if (fd < 0)
return -1;
pim_socket_ip_hdr(fd);
if (interface == NULL) {
ret = pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0);
if (ret != 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Dropping mtrace packet, "
"no route to destination");
return -1;
}
if_out = nexthop.interface;
} else {
if_out = interface;
}
ret = pim_socket_bind(fd, if_out);
if (ret < 0) {
close(fd);
return -1;
}
memset(&to, 0, sizeof(to));
to.sin_family = AF_INET;
to.sin_addr = ip_hdr->ip_dst;
tolen = sizeof(to);
sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0,
(struct sockaddr *)&to, tolen);
close(fd);
if (sent < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Failed to forward mtrace packet:"
" sendto errno=%d, %s",
errno, safe_strerror(errno));
return -1;
}
if (PIM_DEBUG_MTRACE) {
zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u",
ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst),
ip_hdr->ip_ttl);
}
return 0;
}
static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
{
struct prefix_sg sg;
struct channel_oil *c_oil;
struct listnode *chnode;
struct listnode *chnextnode;
struct pim_ifchannel *ch = NULL;
int ret = -1;
memset(&sg, 0, sizeof(struct prefix_sg));
sg.grp = ip_hdr->ip_dst;
c_oil = pim_find_channel_oil(pim, &sg);
if (c_oil == NULL) {
if (PIM_DEBUG_MTRACE) {
zlog_debug(
"Dropping mtrace multicast packet "
"len=%u to %s ttl=%u",
ntohs(ip_hdr->ip_len),
inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl);
}
return -1;
}
if (c_oil->up == NULL)
return -1;
if (c_oil->up->ifchannels == NULL)
return -1;
for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
if (pim_macro_chisin_oiflist(ch)) {
int r;
r = mtrace_un_forward_packet(pim, ip_hdr,
ch->interface);
if (r == 0)
ret = 0;
}
}
return ret;
}
static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
{
if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
return mtrace_mc_forward_packet(pim, ip_hdr);
else
return mtrace_un_forward_packet(pim, ip_hdr, NULL);
}
/* 6.5 Sending Traceroute Responses */
static int mtrace_send_mc_response(struct pim_instance *pim,
struct igmp_mtrace *mtracep,
size_t mtrace_len)
{
struct prefix_sg sg;
struct channel_oil *c_oil;
struct listnode *chnode;
struct listnode *chnextnode;
struct pim_ifchannel *ch = NULL;
int ret = -1;
memset(&sg, 0, sizeof(struct prefix_sg));
sg.grp = mtracep->rsp_addr;
c_oil = pim_find_channel_oil(pim, &sg);
if (c_oil == NULL) {
if (PIM_DEBUG_MTRACE) {
zlog_debug(
"Dropping mtrace multicast response packet "
"len=%u to %s",
(unsigned int)mtrace_len,
inet_ntoa(mtracep->rsp_addr));
}
return -1;
}
if (c_oil->up == NULL)
return -1;
if (c_oil->up->ifchannels == NULL)
return -1;
for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
if (pim_macro_chisin_oiflist(ch)) {
int r;
r = mtrace_send_packet(ch->interface, mtracep,
mtrace_len, mtracep->rsp_addr,
mtracep->grp_addr);
if (r == 0)
ret = 0;
}
}
return ret;
}
static int mtrace_send_response(struct pim_instance *pim,
struct igmp_mtrace *mtracep, size_t mtrace_len)
{
struct pim_nexthop nexthop;
int ret;
mtracep->type = PIM_IGMP_MTRACE_RESPONSE;
mtracep->checksum = 0;
mtracep->checksum = in_cksum((char *)mtracep, mtrace_len);
if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
struct pim_rpf *p_rpf;
char grp_str[INET_ADDRSTRLEN];
if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
return mtrace_send_mc_response(pim, mtracep,
mtrace_len);
p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
if (p_rpf == NULL) {
if (PIM_DEBUG_MTRACE)
zlog_warn("mtrace no RP for %s",
inet_ntop(AF_INET,
&(mtracep->rsp_addr),
grp_str, sizeof(grp_str)));
return -1;
}
nexthop = p_rpf->source_nexthop;
if (PIM_DEBUG_MTRACE)
zlog_debug("mtrace response to RP");
} else {
/* TODO: should use unicast rib lookup */
ret = pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1);
if (ret != 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Dropped response qid=%ud, no route to "
"response address",
mtracep->qry_id);
return -1;
}
}
return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len,
mtracep->rsp_addr, mtracep->grp_addr);
}
int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len)
{
static uint32_t qry_id, qry_src;
char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE];
struct pim_nexthop nexthop;
struct interface *ifp;
struct interface *out_ifp;
struct pim_interface *pim_ifp;
struct pim_interface *pim_out_ifp;
struct pim_instance *pim;
struct igmp_mtrace *mtracep;
struct igmp_mtrace_rsp *rspp;
struct in_addr nh_addr;
enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR;
int ret;
size_t r_len;
int last_rsp_ind = 0;
size_t mtrace_len;
uint16_t recv_checksum;
uint16_t checksum;
ifp = igmp->interface;
pim_ifp = ifp->info;
pim = pim_ifp->pim;
/*
* 6. Router Behaviour
* Check if mtrace packet is addressed elsewhere and forward,
* if applicable
*/
if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
if (!if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET,
pim->vrf_id))
return mtrace_forward_packet(pim, ip_hdr);
if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s: too short,"
" len=%d, min=%zu",
from_str, ifp->name, igmp_msg_len,
sizeof(struct igmp_mtrace));
return -1;
}
mtracep = (struct igmp_mtrace *)igmp_msg;
recv_checksum = mtracep->checksum;
mtracep->checksum = 0;
checksum = in_cksum(igmp_msg, igmp_msg_len);
if (recv_checksum != checksum) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s: checksum"
" mismatch: received=%x computed=%x",
from_str, ifp->name, recv_checksum, checksum);
return -1;
}
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
/* subtract header from message length */
r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
/* Classify mtrace packet, check if it is a query */
if (!r_len) {
if (PIM_DEBUG_MTRACE)
zlog_debug("Received IGMP multicast traceroute query");
/* 6.1.1 Packet verification */
if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) {
if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Dropping multicast query "
"on wrong interface");
return -1;
}
/* Unicast query on wrong interface */
fwd_code = MTRACE_FWD_CODE_WRONG_IF;
}
if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
"Dropping multicast query with "
"duplicate source and id");
return -1;
}
qry_id = mtracep->qry_id;
qry_src = from.s_addr;
}
/* if response fields length is equal to a whole number of responses */
else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) {
r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
if (r_len != 0)
last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp);
if (last_rsp_ind > MTRACE_MAX_HOPS) {
if (PIM_DEBUG_MTRACE)
zlog_warn("Mtrace request of excessive size");
return -1;
}
} else {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s: "
"invalid length %d",
from_str, ifp->name, igmp_msg_len);
return -1;
}
/* 6.2.1 Packet Verification - drop not link-local multicast */
if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))
&& !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace packet from %s on %s:"
" not link-local multicast %s",
from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst));
return -1;
}
/* 6.2.2. Normal Processing */
/* 6.2.2. 1. */
if (last_rsp_ind == MTRACE_MAX_HOPS) {
mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code =
MTRACE_FWD_CODE_NO_SPACE;
return mtrace_send_response(pim_ifp->pim, mtracep,
igmp_msg_len);
}
/* calculate new mtrace mtrace lenght with extra response */
mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp);
/* copy received query/request */
memcpy(mtrace_buf, igmp_msg, igmp_msg_len);
/* repoint mtracep pointer to copy */
mtracep = (struct igmp_mtrace *)mtrace_buf;
/* pointer for extra response field to be filled in */
rspp = &mtracep->rsp[last_rsp_ind];
/* initialize extra response field */
mtrace_rsp_init(rspp);
rspp->arrival = htonl(query_arrival_time());
rspp->outgoing = pim_ifp->primary_address;
rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
/* 6.2.2. 2. Attempt to determine forwarding information */
nh_addr.s_addr = 0;
ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1);
if (ret == 0) {
char nexthop_str[INET_ADDRSTRLEN];
if (PIM_DEBUG_MTRACE)
zlog_debug("mtrace pim_nexthop_lookup OK");
if (PIM_DEBUG_MTRACE)
zlog_warn("mtrace next_hop=%s",
inet_ntop(nexthop.mrib_nexthop_addr.family,
&nexthop.mrib_nexthop_addr.u.prefix,
nexthop_str, sizeof(nexthop_str)));
if (nexthop.mrib_nexthop_addr.family == AF_INET)
nh_addr = nexthop.mrib_nexthop_addr.u.prefix4;
}
/* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */
else {
if (PIM_DEBUG_MTRACE)
zlog_debug("mtrace not found neighbor");
if (!fwd_code)
rspp->fwd_code = MTRACE_FWD_CODE_NO_ROUTE;
else
rspp->fwd_code = fwd_code;
/* 6.5 Sending Traceroute Responses */
return mtrace_send_response(pim, mtracep, mtrace_len);
}
out_ifp = nexthop.interface;
pim_out_ifp = out_ifp->info;
rspp->incoming = pim_out_ifp->primary_address;
rspp->prev_hop = nh_addr;
rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
rspp->s = 1;
rspp->src_mask = 32;
if (nh_addr.s_addr == 0) {
/* reached source? */
if (pim_if_connected_to_source(out_ifp, mtracep->src_addr))
return mtrace_send_response(pim, mtracep, mtrace_len);
/*
* 6.4 Forwarding Traceroute Requests:
* Previous-hop router not known
*/
inet_aton(MCAST_ALL_ROUTERS, &nh_addr);
}
if (mtracep->hops <= (last_rsp_ind + 1))
return mtrace_send_response(pim, mtracep, mtrace_len);
mtracep->checksum = 0;
mtracep->checksum = in_cksum(mtrace_buf, mtrace_len);
return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr,
mtracep->grp_addr);
}
int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len)
{
static uint32_t qry_id, rsp_dst;
struct interface *ifp;
struct pim_interface *pim_ifp;
struct pim_instance *pim;
struct igmp_mtrace *mtracep;
uint16_t recv_checksum;
uint16_t checksum;
ifp = igmp->interface;
pim_ifp = ifp->info;
pim = pim_ifp->pim;
mtracep = (struct igmp_mtrace *)igmp_msg;
recv_checksum = mtracep->checksum;
mtracep->checksum = 0;
checksum = in_cksum(igmp_msg, igmp_msg_len);
if (recv_checksum != checksum) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
"Recv mtrace response from %s on %s: checksum"
" mismatch: received=%x computed=%x",
from_str, ifp->name, recv_checksum, checksum);
return -1;
}
mtracep->checksum = checksum;
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
/* Drop duplicate packets */
if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) {
if (PIM_DEBUG_MTRACE)
zlog_debug("duplicate mtrace response packet dropped");
return -1;
}
qry_id = mtracep->qry_id;
rsp_dst = ip_hdr->ip_dst.s_addr;
return mtrace_forward_packet(pim, ip_hdr);
}

103
pimd/pim_igmp_mtrace.h Normal file
View File

@ -0,0 +1,103 @@
/*
* Multicast traceroute for FRRouting
* Copyright (C) 2017 Mladen Sablic
*
* 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 PIM_IGMP_MTRACE_H
#define PIM_IGMP_MTRACE_H
#include <zebra.h>
#include "pim_igmp.h"
#define MTRACE_MAX_HOPS (255)
#define MTRACE_UNKNOWN_COUNT (0xffffffff)
enum mtrace_fwd_code {
MTRACE_FWD_CODE_NO_ERROR = 0x00,
MTRACE_FWD_CODE_WRONG_IF = 0x01,
MTRACE_FWD_CODE_PRUNE_SENT = 0x02,
MTRACE_FWD_CODE_PRUNE_RCVD = 0x03,
MTRACE_FWD_CODE_SCOPED = 0x04,
MTRACE_FWD_CODE_NO_ROUTE = 0x05,
MTRACE_FWD_CODE_WRONG_LAST_HOP = 0x06,
MTRACE_FWD_CODE_NOT_FORWARDING = 0x07,
MTRACE_FWD_CODE_REACHED_RP = 0x08,
MTRACE_FWD_CODE_RPF_IF = 0x09,
MTRACE_FWD_CODE_NO_MULTICAST = 0x0A,
MTRACE_FWD_CODE_INFO_HIDDEN = 0x0B,
MTRACE_FWD_CODE_NO_SPACE = 0x81,
MTRACE_FWD_CODE_OLD_ROUTER = 0x82,
MTRACE_FWD_CODE_ADMIN_PROHIB = 0x83
};
enum mtrace_rtg_proto {
MTRACE_RTG_PROTO_DVMRP = 1,
MTRACE_RTG_PROTO_MOSPF = 2,
MTRACE_RTG_PROTO_PIM = 3,
MTRACE_RTG_PROTO_CBT = 4,
MTRACE_RTG_PROTO_PIM_SPECIAL = 5,
MTRACE_RTG_PROTO_PIM_STATIC = 6,
MTRACE_RTG_PROTO_DVMRP_STATIC = 7,
MTRACE_RTG_PROTO_PIM_MBGP = 8,
MTRACE_RTG_PROTO_CBT_SPECIAL = 9,
MTRACE_RTG_PROTO_CBT_STATIC = 10,
MTRACE_RTG_PROTO_PIM_ASSERT = 11,
};
struct igmp_mtrace_rsp {
uint32_t arrival;
struct in_addr incoming;
struct in_addr outgoing;
struct in_addr prev_hop;
uint32_t in_count;
uint32_t out_count;
uint32_t total;
uint32_t rtg_proto : 8;
uint32_t fwd_ttl : 8;
/* little endian order for next three fields */
uint32_t src_mask : 6;
uint32_t s : 1;
uint32_t mbz : 1;
uint32_t fwd_code : 8;
} __attribute__((packed));
struct igmp_mtrace {
uint8_t type;
uint8_t hops;
uint16_t checksum;
struct in_addr grp_addr;
struct in_addr src_addr;
struct in_addr dst_addr;
struct in_addr rsp_addr;
uint32_t rsp_ttl : 8;
uint32_t qry_id : 24;
struct igmp_mtrace_rsp rsp[0];
} __attribute__((packed));
#define MTRACE_HDR_SIZE (sizeof(struct igmp_mtrace))
#define MTRACE_RSP_SIZE (sizeof(struct igmp_mtrace_rsp))
int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len);
int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
struct in_addr from, const char *from_str,
char *igmp_msg, int igmp_msg_len);
#endif /* PIM_IGMP_MTRACE_H */

View File

@ -135,8 +135,8 @@ void pim_channel_oil_free(struct channel_oil *c_oil)
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
}
static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
struct prefix_sg *sg)
struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
struct prefix_sg *sg)
{
struct channel_oil *c_oil = NULL;
struct channel_oil lookup;

View File

@ -86,6 +86,8 @@ void pim_oil_init(struct pim_instance *pim);
void pim_oil_terminate(struct pim_instance *pim);
void pim_channel_oil_free(struct channel_oil *c_oil);
struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
struct prefix_sg *sg);
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
struct prefix_sg *sg,
int input_vif_index);

View File

@ -78,6 +78,11 @@ int pim_debug_config_write(struct vty *vty)
++writes;
}
if (PIM_DEBUG_MTRACE) {
vty_out(vty, "debug mtrace\n");
++writes;
}
if (PIM_DEBUG_MROUTE_DETAIL) {
vty_out(vty, "debug mroute detail\n");
++writes;

View File

@ -112,6 +112,7 @@
#define PIM_MASK_PIM_NHT (1 << 22)
#define PIM_MASK_PIM_NHT_DETAIL (1 << 23)
#define PIM_MASK_PIM_NHT_RP (1 << 24)
#define PIM_MASK_MTRACE (1 << 25)
/* Remember 32 bits!!! */
/* PIM error codes */
@ -188,6 +189,7 @@ extern int32_t qpim_register_probe_time;
#define PIM_DEBUG_PIM_NHT (qpim_debugs & PIM_MASK_PIM_NHT)
#define PIM_DEBUG_PIM_NHT_DETAIL (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL)
#define PIM_DEBUG_PIM_NHT_RP (qpim_debugs & PIM_MASK_PIM_NHT_RP)
#define PIM_DEBUG_MTRACE (qpim_debugs & PIM_MASK_MTRACE)
#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS))
#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS))
@ -216,6 +218,7 @@ extern int32_t qpim_register_probe_time;
#define PIM_DO_DEBUG_MSDP_INTERNAL (qpim_debugs |= PIM_MASK_MSDP_INTERNAL)
#define PIM_DO_DEBUG_PIM_NHT (qpim_debugs |= PIM_MASK_PIM_NHT)
#define PIM_DO_DEBUG_PIM_NHT_RP (qpim_debugs |= PIM_MASK_PIM_NHT_RP)
#define PIM_DO_DEBUG_MTRACE (qpim_debugs |= PIM_MASK_MTRACE)
#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
@ -240,6 +243,7 @@ extern int32_t qpim_register_probe_time;
#define PIM_DONT_DEBUG_MSDP_INTERNAL (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL)
#define PIM_DONT_DEBUG_PIM_NHT (qpim_debugs &= ~PIM_MASK_PIM_NHT)
#define PIM_DONT_DEBUG_PIM_NHT_RP (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP)
#define PIM_DONT_DEBUG_MTRACE (qpim_debugs &= ~PIM_MASK_MTRACE)
void pim_init(void);
void pim_terminate(void);

View File

@ -5,6 +5,7 @@
if PIMD
noinst_LIBRARIES += pimd/libpim.a
sbin_PROGRAMS += pimd/pimd
bin_PROGRAMS += pimd/mtracebis
noinst_PROGRAMS += pimd/test_igmpv3_join
dist_examples_DATA += pimd/pimd.conf.sample
endif
@ -18,6 +19,7 @@ pimd_libpim_a_SOURCES = \
pimd/pim_iface.c \
pimd/pim_ifchannel.c \
pimd/pim_igmp.c \
pimd/pim_igmp_mtrace.c \
pimd/pim_igmpv2.c \
pimd/pim_igmpv3.c \
pimd/pim_instance.c \
@ -66,6 +68,7 @@ noinst_HEADERS += \
pimd/pim_ifchannel.h \
pimd/pim_igmp.h \
pimd/pim_igmp_join.h \
pimd/pim_igmp_mtrace.h \
pimd/pim_igmpv2.h \
pimd/pim_igmpv3.h \
pimd/pim_instance.h \
@ -101,6 +104,8 @@ noinst_HEADERS += \
pimd/pim_zebra.h \
pimd/pim_zlookup.h \
pimd/pimd.h \
pimd/mtracebis_netlink.h \
pimd/mtracebis_routeget.h \
# end
pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@
@ -108,3 +113,9 @@ pimd_pimd_SOURCES = pimd/pim_main.c
pimd_test_igmpv3_join_LDADD = lib/libfrr.la
pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
pimd_mtracebis_LDADD = lib/libfrr.la
pimd_mtracebis_SOURCES = pimd/mtracebis.c \
pimd/mtracebis_netlink.c \
pimd/mtracebis_routeget.c \
# end

View File

@ -80,14 +80,17 @@ DEFPY (install_routes,
}
DEFPY(vrf_label, vrf_label_cmd,
"sharp label vrf NAME$name label (0-100000)$label",
"sharp label <ip$ipv4|ipv6$ipv6> vrf NAME$name label (0-100000)$label",
"Sharp Routing Protocol\n"
"Give a vrf a label\n"
"Pop and forward for IPv4\n"
"Pop and forward for IPv6\n"
VRF_CMD_HELP_STR
"The label to use, 0 specifies remove the label installed from previous\n"
"Specified range to use\n")
{
struct vrf *vrf;
afi_t afi = (ipv4) ? AFI_IP : AFI_IP6;
if (strcmp(name, "default") == 0)
vrf = vrf_lookup_by_id(VRF_DEFAULT);
@ -102,7 +105,7 @@ DEFPY(vrf_label, vrf_label_cmd,
if (label == 0)
label = MPLS_LABEL_NONE;
vrf_label_add(vrf->vrf_id, label);
vrf_label_add(vrf->vrf_id, afi, label);
return CMD_SUCCESS;
}

View File

@ -131,13 +131,14 @@ static int interface_state_down(int command, struct zclient *zclient,
extern uint32_t total_routes;
extern uint32_t installed_routes;
static int notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
static int route_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct prefix p;
enum zapi_route_notify_owner note;
uint32_t table;
if (!zapi_route_notify_decode(zclient->ibuf, &p, &note))
if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note))
return -1;
installed_routes++;
@ -152,9 +153,9 @@ static void zebra_connected(struct zclient *zclient)
zclient_send_reg_requests(zclient, VRF_DEFAULT);
}
void vrf_label_add(vrf_id_t vrf_id, mpls_label_t label)
void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label)
{
zclient_send_vrf_label(zclient, vrf_id, label, ZEBRA_LSP_SHARP);
zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP);
}
void route_add(struct prefix *p, struct nexthop *nh)
@ -168,6 +169,7 @@ void route_add(struct prefix *p, struct nexthop *nh)
api.safi = SAFI_UNICAST;
memcpy(&api.prefix, p, sizeof(*p));
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api_nh = &api.nexthops[0];
@ -210,5 +212,5 @@ void sharp_zebra_init(void)
zclient->interface_down = interface_state_down;
zclient->interface_address_add = interface_address_add;
zclient->interface_address_delete = interface_address_delete;
zclient->notify_owner = notify_owner;
zclient->route_notify_owner = route_notify_owner;
}

Some files were not shown because too many files have changed in this diff Show More