mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-08 05:27:47 +00:00
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:
commit
a975744835
30
COMMUNITY.md
30
COMMUNITY.md
@ -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
|
annotations to print warnings at compile time, pointing to the appropriate
|
||||||
update path. A `-Werror` build should fail if compatibility bits are used.
|
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
|
### Miscellaneous
|
||||||
|
|
||||||
When in doubt, follow the guidelines in the Linux kernel style guide, or ask on
|
When in doubt, follow the guidelines in the Linux kernel style guide, or ask on
|
||||||
|
@ -465,7 +465,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (peer->su.sa.sa_family == AF_INET) {
|
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_putw(obuf, AFI_IP);
|
||||||
|
|
||||||
stream_put(obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
|
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);
|
stream_put(obuf, empty, IPV4_MAX_BYTELEN);
|
||||||
} else if (peer->su.sa.sa_family == AF_INET6) {
|
} else if (peer->su.sa.sa_family == AF_INET6) {
|
||||||
/* Interface Index and Address family. */
|
/* Interface Index and Address family. */
|
||||||
stream_putw(obuf, peer->ifindex);
|
stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
|
||||||
stream_putw(obuf, AFI_IP6);
|
stream_putw(obuf, AFI_IP6);
|
||||||
|
|
||||||
/* Source IP Address and Destination IP Address. */
|
/* Source IP Address and Destination IP Address. */
|
||||||
|
@ -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))
|
for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
|
||||||
attr->ecommunity = ecommunity_merge(attr->ecommunity, 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);
|
vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
|
||||||
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
|
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
|
||||||
for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
|
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);
|
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));
|
memset(&ecom_rmac, 0, sizeof(ecom_rmac));
|
||||||
encode_rmac_extcomm(&eval_rmac, &attr->rmac);
|
encode_rmac_extcomm(&eval_rmac, &attr->rmac);
|
||||||
ecom_rmac.size = 1;
|
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 */
|
/* The VNI goes into the 'label' field of the route */
|
||||||
vni2label(vpn->vni, &label[0]);
|
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;
|
vni_t l3vni;
|
||||||
|
|
||||||
l3vni = bgpevpn_get_l3vni(vpn);
|
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))
|
&& !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
|
||||||
route_change = 0;
|
route_change = 0;
|
||||||
else {
|
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. */
|
/* The attribute has changed. */
|
||||||
/* Add (or update) attribute to hash. */
|
/* Add (or update) attribute to hash. */
|
||||||
attr_new = bgp_attr_intern(attr);
|
attr_new = bgp_attr_intern(attr);
|
||||||
@ -1254,7 +1283,13 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
|
|||||||
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
|
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
|
||||||
attr.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0;
|
attr.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0;
|
||||||
attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0;
|
attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0;
|
||||||
|
|
||||||
|
/* 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);
|
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);
|
bgpevpn_get_rmac(vpn, &attr.rmac);
|
||||||
vni2label(vpn->vni, &(attr.label));
|
vni2label(vpn->vni, &(attr.label));
|
||||||
|
|
||||||
@ -3275,6 +3310,19 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
|
|||||||
*/
|
*/
|
||||||
for (ri = rn->info; ri; ri = ri->next) {
|
for (ri = rn->info; ri; ri = ri->next) {
|
||||||
if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)) {
|
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,
|
bgp_evpn_advertise_type5_route(bgp_vrf, &rn->p,
|
||||||
ri->attr,
|
ri->attr,
|
||||||
afi, safi);
|
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,
|
int bgp_evpn_local_l3vni_add(vni_t l3vni,
|
||||||
vrf_id_t vrf_id,
|
vrf_id_t vrf_id,
|
||||||
struct ethaddr *rmac,
|
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_vrf = NULL; /* bgp VRF instance */
|
||||||
struct bgp *bgp_def = NULL; /* default bgp 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 */
|
/* set the originator ip */
|
||||||
bgp_vrf->originator_ip = 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 */
|
/* auto derive RD/RT */
|
||||||
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
|
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
|
||||||
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
|
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
|
||||||
@ -4279,7 +4332,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
|
|||||||
link_l2vni_hash_to_l3vni,
|
link_l2vni_hash_to_l3vni,
|
||||||
bgp_vrf);
|
bgp_vrf);
|
||||||
|
|
||||||
/* updates all corresponding local mac-ip routes */
|
/* 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))
|
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
|
||||||
update_routes_for_vni(bgp_def, vpn);
|
update_routes_for_vni(bgp_def, vpn);
|
||||||
|
|
||||||
@ -4340,9 +4396,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* update all corresponding local mac-ip routes */
|
/* update all corresponding local mac-ip routes */
|
||||||
for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, 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);
|
update_routes_for_vni(bgp_def, vpn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Delete the instance if it was autocreated */
|
/* Delete the instance if it was autocreated */
|
||||||
if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
|
if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
|
||||||
|
@ -91,7 +91,8 @@ extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
|
|||||||
u_char flags);
|
u_char flags);
|
||||||
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
|
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
|
||||||
struct ethaddr *rmac,
|
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_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_del(struct bgp *bgp, vni_t vni);
|
||||||
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
|
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
|
||||||
|
@ -61,6 +61,10 @@ struct bgpevpn {
|
|||||||
#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
|
#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
|
||||||
#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT 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_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*/
|
/* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
|
||||||
u_int8_t advertise_gw_macip;
|
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)
|
static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn)
|
||||||
{
|
{
|
||||||
struct bgp *bgp_vrf = NULL;
|
return vpn->bgp_vrf ? vpn->bgp_vrf->l3vni : 0;
|
||||||
|
|
||||||
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
|
|
||||||
if (!bgp_vrf)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return bgp_vrf->l3vni;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac)
|
static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac)
|
||||||
{
|
{
|
||||||
struct bgp *bgp_vrf = NULL;
|
|
||||||
|
|
||||||
memset(rmac, 0, sizeof(struct ethaddr));
|
memset(rmac, 0, sizeof(struct ethaddr));
|
||||||
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
|
if (!vpn->bgp_vrf)
|
||||||
if (!bgp_vrf)
|
|
||||||
return;
|
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)
|
static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn)
|
||||||
{
|
{
|
||||||
struct bgp *bgp_vrf = NULL;
|
if (!vpn->bgp_vrf)
|
||||||
|
|
||||||
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
|
|
||||||
if (!bgp_vrf)
|
|
||||||
return NULL;
|
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)
|
static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn)
|
||||||
{
|
{
|
||||||
struct bgp *bgp_vrf = NULL;
|
if (!vpn->bgp_vrf)
|
||||||
|
|
||||||
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
|
|
||||||
if (!bgp_vrf)
|
|
||||||
return NULL;
|
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)
|
static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
|
||||||
{
|
{
|
||||||
struct bgp *bgp_vrf = NULL;
|
/* bail if vpn is not associated to bgp_vrf */
|
||||||
|
if (!vpn->bgp_vrf)
|
||||||
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
|
|
||||||
if (!bgp_vrf || !bgp_vrf->l2vnis)
|
|
||||||
return;
|
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)
|
static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
|
||||||
{
|
{
|
||||||
struct bgp *bgp_vrf = NULL;
|
struct bgp *bgp_vrf = NULL;
|
||||||
|
|
||||||
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
|
/* bail if vpn is already associated to vrf */
|
||||||
if (!bgp_vrf || !bgp_vrf->l2vnis)
|
if (vpn->bgp_vrf)
|
||||||
return;
|
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);
|
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)
|
static inline int is_vni_configured(struct bgpevpn *vpn)
|
||||||
|
@ -83,7 +83,7 @@ static void display_vrf_import_rt(struct vty *vty,
|
|||||||
case ECOMMUNITY_ENCODE_AS:
|
case ECOMMUNITY_ENCODE_AS:
|
||||||
eas.as = (*pnt++ << 8);
|
eas.as = (*pnt++ << 8);
|
||||||
eas.as |= (*pnt++);
|
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);
|
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:
|
case ECOMMUNITY_ENCODE_AS:
|
||||||
eas.as = (*pnt++ << 8);
|
eas.as = (*pnt++ << 8);
|
||||||
eas.as |= (*pnt++);
|
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);
|
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);
|
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,
|
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
|
||||||
struct vty *vty, struct in_addr vtep_ip,
|
struct vty *vty, struct in_addr vtep_ip,
|
||||||
json_object *json)
|
json_object *json)
|
||||||
@ -2723,19 +2676,34 @@ DEFUN (no_bgp_evpn_advertise_vni_subnet,
|
|||||||
|
|
||||||
DEFUN (bgp_evpn_advertise_type5,
|
DEFUN (bgp_evpn_advertise_type5,
|
||||||
bgp_evpn_advertise_type5_cmd,
|
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"
|
"Advertise prefix routes\n"
|
||||||
BGP_AFI_HELP_STR
|
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 */
|
struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
|
||||||
int idx_afi = 0;
|
int idx_afi = 0;
|
||||||
int idx_safi = 0;
|
int idx_safi = 0;
|
||||||
|
int idx_rmap = 0;
|
||||||
afi_t afi = 0;
|
afi_t afi = 0;
|
||||||
safi_t safi = 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_afi(argv, argc, &idx_afi, &afi);
|
||||||
argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
|
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)) {
|
if (!(afi == AFI_IP) || (afi == AFI_IP6)) {
|
||||||
vty_out(vty,
|
vty_out(vty,
|
||||||
@ -2754,24 +2722,44 @@ DEFUN (bgp_evpn_advertise_type5,
|
|||||||
/* if we are already advertising ipv4 prefix as type-5
|
/* if we are already advertising ipv4 prefix as type-5
|
||||||
* nothing to do
|
* nothing to do
|
||||||
*/
|
*/
|
||||||
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
|
if (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags,
|
||||||
BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
|
BGP_VRF_ADVERTISE_IPV4_IN_EVPN))
|
||||||
|
return CMD_WARNING;
|
||||||
SET_FLAG(bgp_vrf->vrf_flags,
|
SET_FLAG(bgp_vrf->vrf_flags,
|
||||||
BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
|
BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
|
||||||
bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* if we are already advertising ipv6 prefix as type-5
|
/* if we are already advertising ipv6 prefix as type-5
|
||||||
* nothing to do
|
* nothing to do
|
||||||
*/
|
*/
|
||||||
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
|
if (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags,
|
||||||
BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
|
BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
|
||||||
|
return CMD_WARNING;
|
||||||
SET_FLAG(bgp_vrf->vrf_flags,
|
SET_FLAG(bgp_vrf->vrf_flags,
|
||||||
BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
|
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);
|
bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
|
||||||
}
|
|
||||||
}
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2827,6 +2815,15 @@ DEFUN (no_bgp_evpn_advertise_type5,
|
|||||||
BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
|
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;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2923,16 +2920,23 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
|
|||||||
*/
|
*/
|
||||||
DEFUN(show_bgp_l2vpn_evpn_summary,
|
DEFUN(show_bgp_l2vpn_evpn_summary,
|
||||||
show_bgp_l2vpn_evpn_summary_cmd,
|
show_bgp_l2vpn_evpn_summary_cmd,
|
||||||
"show bgp l2vpn evpn summary [json]",
|
"show bgp [vrf VRFNAME] l2vpn evpn summary [json]",
|
||||||
SHOW_STR
|
SHOW_STR
|
||||||
BGP_STR
|
BGP_STR
|
||||||
|
"bgp vrf\n"
|
||||||
|
"vrf name\n"
|
||||||
L2VPN_HELP_STR
|
L2VPN_HELP_STR
|
||||||
EVPN_HELP_STR
|
EVPN_HELP_STR
|
||||||
"Summary of BGP neighbor status\n"
|
"Summary of BGP neighbor status\n"
|
||||||
JSON_STR)
|
JSON_STR)
|
||||||
{
|
{
|
||||||
|
int idx_vrf = 0;
|
||||||
u_char uj = use_json(argc, argv);
|
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;
|
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.
|
* 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, " L3-VNI: %u\n", bgp->l3vni);
|
||||||
vty_out(vty, " Rmac: %s\n",
|
vty_out(vty, " Rmac: %s\n",
|
||||||
prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
|
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, " L2-VNI List:\n");
|
||||||
vty_out(vty, " ");
|
vty_out(vty, " ");
|
||||||
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
|
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",
|
json_object_string_add(json, "rmac",
|
||||||
prefix_mac2str(&bgp->rmac, buf,
|
prefix_mac2str(&bgp->rmac, buf,
|
||||||
sizeof(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 */
|
/* list of l2vnis */
|
||||||
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
|
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
|
||||||
json_object_array_add(json_vnis,
|
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,
|
void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
|
||||||
safi_t safi)
|
safi_t safi)
|
||||||
{
|
{
|
||||||
|
char buf1[RD_ADDRSTRLEN];
|
||||||
|
|
||||||
if (bgp->vnihash)
|
if (bgp->vnihash)
|
||||||
hash_iterate(bgp->vnihash,
|
hash_iterate(bgp->vnihash,
|
||||||
(void (*)(struct hash_backet *,
|
(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))
|
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
|
||||||
vty_out(vty, " advertise ipv6 unicast\n");
|
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)
|
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_cmd);
|
||||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_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_vni_cmd);
|
||||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd);
|
|
||||||
install_element(VIEW_NODE,
|
install_element(VIEW_NODE,
|
||||||
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
|
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
|
||||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
|
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
|
||||||
|
@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer)
|
|||||||
/* First entry point of peer's finite state machine. In Idle
|
/* First entry point of peer's finite state machine. In Idle
|
||||||
status start timer is on unless peer is shutdown or peer is
|
status start timer is on unless peer is shutdown or peer is
|
||||||
inactive. All other timer must be turned off */
|
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);
|
BGP_TIMER_OFF(peer->t_start);
|
||||||
} else {
|
} else {
|
||||||
BGP_TIMER_ON(peer->t_start, bgp_start_timer,
|
BGP_TIMER_ON(peer->t_start, bgp_start_timer,
|
||||||
@ -1376,6 +1377,15 @@ int bgp_start(struct peer *peer)
|
|||||||
return 0;
|
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 */
|
/* Register to be notified on peer up */
|
||||||
if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
|
if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
|
||||||
&& !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|
&& !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|
||||||
|
@ -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 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;
|
*next_update = diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "vrf.h"
|
#include "vrf.h"
|
||||||
#include "bfd.h"
|
#include "bfd.h"
|
||||||
#include "libfrr.h"
|
#include "libfrr.h"
|
||||||
|
#include "ns.h"
|
||||||
|
|
||||||
#include "bgpd/bgpd.h"
|
#include "bgpd/bgpd.h"
|
||||||
#include "bgpd/bgp_attr.h"
|
#include "bgpd/bgp_attr.h"
|
||||||
@ -57,6 +58,7 @@
|
|||||||
#include "bgpd/bgp_zebra.h"
|
#include "bgpd/bgp_zebra.h"
|
||||||
#include "bgpd/bgp_packet.h"
|
#include "bgpd/bgp_packet.h"
|
||||||
#include "bgpd/bgp_keepalives.h"
|
#include "bgpd/bgp_keepalives.h"
|
||||||
|
#include "bgpd/bgp_network.h"
|
||||||
|
|
||||||
#ifdef ENABLE_BGP_VNC
|
#ifdef ENABLE_BGP_VNC
|
||||||
#include "bgpd/rfapi/rfapi_backend.h"
|
#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". */
|
/* We have instance configured, link to VRF and make it "up". */
|
||||||
bgp_vrf_link(bgp, vrf);
|
bgp_vrf_link(bgp, vrf);
|
||||||
|
|
||||||
|
bgp_handle_socket(bgp, vrf, old_vrf_id, true);
|
||||||
/* Update any redistribute vrf bitmaps if the vrf_id changed */
|
/* Update any redistribute vrf bitmaps if the vrf_id changed */
|
||||||
if (old_vrf_id != bgp->vrf_id)
|
if (old_vrf_id != bgp->vrf_id)
|
||||||
bgp_update_redist_vrf_bitmaps(bgp, old_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);
|
bgp = bgp_lookup_by_name(vrf->name);
|
||||||
if (bgp) {
|
if (bgp) {
|
||||||
old_vrf_id = bgp->vrf_id;
|
old_vrf_id = bgp->vrf_id;
|
||||||
|
bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
|
||||||
/* We have instance configured, unlink from VRF and make it
|
/* We have instance configured, unlink from VRF and make it
|
||||||
* "down". */
|
* "down". */
|
||||||
bgp_vrf_unlink(bgp, vrf);
|
bgp_vrf_unlink(bgp, vrf);
|
||||||
|
@ -92,6 +92,26 @@ int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
|
|||||||
return 0;
|
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
|
* 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 &&
|
if (!bi1->attr->mp_nexthop_prefer_global &&
|
||||||
!bi2->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)
|
if (!compare)
|
||||||
compare = IPV6_ADDR_CMP(&addr1, &addr2);
|
compare = IPV6_ADDR_CMP(&addr1, &addr2);
|
||||||
break;
|
break;
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
|
#include "ns.h"
|
||||||
|
|
||||||
#include "bgpd/bgpd.h"
|
#include "bgpd/bgpd.h"
|
||||||
#include "bgpd/bgp_open.h"
|
#include "bgpd/bgp_open.h"
|
||||||
@ -44,13 +45,14 @@
|
|||||||
|
|
||||||
extern struct zebra_privs_t bgpd_privs;
|
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. */
|
/* BGP listening socket. */
|
||||||
struct bgp_listener {
|
struct bgp_listener {
|
||||||
int fd;
|
int fd;
|
||||||
union sockunion su;
|
union sockunion su;
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
|
struct bgp *bgp;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
listener->thread = NULL;
|
listener->thread = NULL;
|
||||||
|
|
||||||
thread_add_read(bm->master, bgp_accept, listener, accept_sock,
|
thread_add_read(bm->master, bgp_accept, listener, accept_sock,
|
||||||
&listener->thread);
|
&listener->thread);
|
||||||
|
|
||||||
@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread)
|
|||||||
}
|
}
|
||||||
set_nonblocking(bgp_sock);
|
set_nonblocking(bgp_sock);
|
||||||
|
|
||||||
/* Obtain BGP instance this connection is meant for. */
|
/* Obtain BGP instance this connection is meant for.
|
||||||
if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
|
* - 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))
|
if (bgp_debug_neighbor_events(NULL))
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"[Event] Could not get instance for incoming conn from %s",
|
"[Event] Could not get instance for incoming conn from %s",
|
||||||
@ -407,7 +415,7 @@ static int bgp_accept(struct thread *thread)
|
|||||||
peer->doppelganger = peer1;
|
peer->doppelganger = peer1;
|
||||||
peer1->doppelganger = peer;
|
peer1->doppelganger = peer;
|
||||||
peer->fd = bgp_sock;
|
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_fsm_change_status(peer, Active);
|
||||||
BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */
|
BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */
|
||||||
|
|
||||||
@ -435,21 +443,20 @@ static int bgp_accept(struct thread *thread)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* BGP socket bind. */
|
/* 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;
|
char *name = NULL;
|
||||||
|
|
||||||
/* If not bound to an interface or part of a VRF, we don't care. */
|
if ((peer->bgp->vrf_id == VRF_DEFAULT) &&
|
||||||
if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if)
|
!peer->ifname && !peer->conf_if)
|
||||||
return 0;
|
return NULL;
|
||||||
|
|
||||||
if (peer->su.sa.sa_family != AF_INET
|
if (peer->su.sa.sa_family != AF_INET
|
||||||
&& peer->su.sa.sa_family != AF_INET6)
|
&& 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)
|
/* For IPv6 peering, interface (unnumbered or link-local with interface)
|
||||||
* takes precedence over VRF. For IPv4 peering, explicit interface or
|
* takes precedence over VRF. For IPv4 peering, explicit interface or
|
||||||
* VRF are the situations to bind.
|
* VRF are the situations to bind.
|
||||||
@ -461,30 +468,7 @@ static int bgp_bind(struct peer *peer)
|
|||||||
else
|
else
|
||||||
name = peer->ifname ? peer->ifname : peer->bgp->name;
|
name = peer->ifname ? peer->ifname : peer->bgp->name;
|
||||||
|
|
||||||
if (!name)
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bgp_update_address(struct interface *ifp, const union sockunion *dst,
|
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");
|
zlog_debug("Peer address not learnt: Returning from connect");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (bgpd_privs.change(ZPRIVS_RAISE))
|
||||||
|
zlog_err("Can't raise privileges");
|
||||||
/* Make socket for the peer. */
|
/* 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)
|
if (peer->fd < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -591,9 +580,6 @@ int bgp_connect(struct peer *peer)
|
|||||||
if (peer->password)
|
if (peer->password)
|
||||||
bgp_md5_set_connect(peer->fd, &peer->su, peer->password);
|
bgp_md5_set_connect(peer->fd, &peer->su, peer->password);
|
||||||
|
|
||||||
/* Bind socket. */
|
|
||||||
bgp_bind(peer);
|
|
||||||
|
|
||||||
/* Update source bind. */
|
/* Update source bind. */
|
||||||
if (bgp_update_source(peer) < 0) {
|
if (bgp_update_source(peer) < 0) {
|
||||||
return connect_error;
|
return connect_error;
|
||||||
@ -642,12 +628,12 @@ int bgp_getsockname(struct peer *peer)
|
|||||||
return -1;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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;
|
struct bgp_listener *listener;
|
||||||
int ret, en;
|
int ret, en;
|
||||||
@ -683,8 +669,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
|
listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
|
||||||
listener->fd = sock;
|
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);
|
memcpy(&listener->su, sa, salen);
|
||||||
listener->thread = NULL;
|
listener->thread = NULL;
|
||||||
thread_add_read(bm->master, bgp_accept, listener, sock,
|
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. */
|
/* 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;
|
||||||
struct addrinfo *ainfo_save;
|
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);
|
snprintf(port_str, sizeof(port_str), "%d", port);
|
||||||
port_str[sizeof(port_str) - 1] = '\0';
|
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) {
|
if (ret != 0) {
|
||||||
zlog_err("getaddrinfo: %s", gai_strerror(ret));
|
zlog_err("getaddrinfo: %s", gai_strerror(ret));
|
||||||
return -1;
|
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)
|
if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sock = socket(ainfo->ai_family, ainfo->ai_socktype,
|
if (bgpd_privs.change(ZPRIVS_RAISE))
|
||||||
ainfo->ai_protocol);
|
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) {
|
if (sock < 0) {
|
||||||
zlog_err("socket: %s", safe_strerror(errno));
|
zlog_err("socket: %s", safe_strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
@ -734,7 +736,8 @@ int bgp_socket(unsigned short port, const char *address)
|
|||||||
* ttl=255 */
|
* ttl=255 */
|
||||||
sockopt_ttl(ainfo->ai_family, sock, MAXTTL);
|
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)
|
if (ret == 0)
|
||||||
++count;
|
++count;
|
||||||
else
|
else
|
||||||
@ -751,6 +754,32 @@ int bgp_socket(unsigned short port, const char *address)
|
|||||||
return 0;
|
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)
|
void bgp_close(void)
|
||||||
{
|
{
|
||||||
struct listnode *node, *next;
|
struct listnode *node, *next;
|
||||||
@ -760,6 +789,8 @@ void bgp_close(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
|
for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
|
||||||
|
if (listener->bgp)
|
||||||
|
continue;
|
||||||
thread_cancel(listener->thread);
|
thread_cancel(listener->thread);
|
||||||
close(listener->fd);
|
close(listener->fd);
|
||||||
listnode_delete(bm->listen_sockets, listener);
|
listnode_delete(bm->listen_sockets, listener);
|
||||||
|
@ -23,7 +23,9 @@
|
|||||||
|
|
||||||
#define BGP_SOCKET_SNDBUF_SIZE 65536
|
#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 void bgp_close(void);
|
||||||
extern int bgp_connect(struct peer *);
|
extern int bgp_connect(struct peer *);
|
||||||
extern int bgp_getsockname(struct peer *);
|
extern int bgp_getsockname(struct peer *);
|
||||||
|
@ -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)
|
static int bgp_route_map_process_update_cb(char *rmap_name)
|
||||||
|
@ -3395,7 +3395,7 @@ DEFUN (no_neighbor_set_peer_group,
|
|||||||
return CMD_WARNING_CONFIG_FAILED;
|
return CMD_WARNING_CONFIG_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = peer_group_unbind(bgp, peer, group);
|
ret = peer_delete(peer);
|
||||||
|
|
||||||
return bgp_vty_return(vty, ret);
|
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;
|
safi = SAFI_MAX;
|
||||||
}
|
}
|
||||||
afi++;
|
afi++;
|
||||||
if (!afi_wildcard
|
if (!afi_wildcard)
|
||||||
|| afi == AFI_L2VPN) /* special case, not handled yet */
|
|
||||||
afi = AFI_MAX;
|
afi = AFI_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
|
||||||
|| info->sub_type == BGP_ROUTE_AGGREGATE) {
|
|| info->sub_type == BGP_ROUTE_AGGREGATE) {
|
||||||
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
|
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)
|
if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
|
||||||
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|
||||||
|| bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
|
|| 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 is currently based on the best-path only */
|
||||||
metric = info->attr->med;
|
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);
|
SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
|
||||||
|
|
||||||
if (peer->sort == BGP_PEER_IBGP) {
|
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);
|
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
|
if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
|
||||||
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|
||||||
|| bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
|
|| 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)) {
|
if (bgp_debug_zebra(p)) {
|
||||||
char buf[PREFIX_STRLEN];
|
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,
|
static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
|
||||||
zebra_size_t length, vrf_id_t vrf_id)
|
zebra_size_t length, vrf_id_t vrf_id)
|
||||||
{
|
{
|
||||||
|
int filter = 0;
|
||||||
char buf[ETHER_ADDR_STRLEN];
|
char buf[ETHER_ADDR_STRLEN];
|
||||||
vni_t l3vni = 0;
|
vni_t l3vni = 0;
|
||||||
struct ethaddr rmac;
|
struct ethaddr rmac;
|
||||||
@ -1739,16 +1740,20 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
|
|||||||
if (cmd == ZEBRA_L3VNI_ADD) {
|
if (cmd == ZEBRA_L3VNI_ADD) {
|
||||||
stream_get(&rmac, s, sizeof(struct ethaddr));
|
stream_get(&rmac, s, sizeof(struct ethaddr));
|
||||||
originator_ip.s_addr = stream_get_ipv4(s);
|
originator_ip.s_addr = stream_get_ipv4(s);
|
||||||
|
stream_get(&filter, s, sizeof(int));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BGP_DEBUG(zebra, ZEBRA))
|
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",
|
(cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
|
||||||
vrf_id_to_name(vrf_id), l3vni,
|
vrf_id_to_name(vrf_id),
|
||||||
prefix_mac2str(&rmac, buf, sizeof(buf)));
|
l3vni,
|
||||||
|
prefix_mac2str(&rmac, buf, sizeof(buf)),
|
||||||
|
filter ? "prefix-routes-only" : "none");
|
||||||
|
|
||||||
if (cmd == ZEBRA_L3VNI_ADD)
|
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
|
else
|
||||||
bgp_evpn_local_l3vni_del(l3vni, vrf_id);
|
bgp_evpn_local_l3vni_del(l3vni, vrf_id);
|
||||||
|
|
||||||
|
151
bgpd/bgpd.c
151
bgpd/bgpd.c
@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp);
|
|||||||
|
|
||||||
extern struct zclient *zclient;
|
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)
|
void bgp_session_reset(struct peer *peer)
|
||||||
{
|
{
|
||||||
if (peer->doppelganger && (peer->doppelganger->status != Deleted)
|
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->config = peer_src->config;
|
||||||
|
|
||||||
peer_dst->local_as = peer_src->local_as;
|
peer_dst->local_as = peer_src->local_as;
|
||||||
peer_dst->ifindex = peer_src->ifindex;
|
|
||||||
peer_dst->port = peer_src->port;
|
peer_dst->port = peer_src->port;
|
||||||
(void)peer_sort(peer_dst);
|
(void)peer_sort(peer_dst);
|
||||||
peer_dst->rmap_type = peer_src->rmap_type;
|
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;
|
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. */
|
/* Called from VTY commands. */
|
||||||
int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
|
int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
|
||||||
enum bgp_instance_type inst_type)
|
enum bgp_instance_type inst_type)
|
||||||
{
|
{
|
||||||
struct bgp *bgp;
|
struct bgp *bgp;
|
||||||
|
struct vrf *vrf = NULL;
|
||||||
|
|
||||||
/* Multiple instance check. */
|
/* Multiple instance check. */
|
||||||
if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) {
|
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;
|
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 Default instance or VRF, link to the VRF structure, if present. */
|
||||||
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
|
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
|
||||||
|| bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
|
|| bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
|
||||||
struct vrf *vrf;
|
|
||||||
|
|
||||||
vrf = bgp_vrf_lookup_by_instance_type(bgp);
|
vrf = bgp_vrf_lookup_by_instance_type(bgp);
|
||||||
if (vrf)
|
if (vrf)
|
||||||
bgp_vrf_link(bgp, 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))
|
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
|
||||||
bgp_zebra_instance_register(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.
|
* routes to be processed still referencing the struct bgp.
|
||||||
*/
|
*/
|
||||||
listnode_delete(bm->bgp, bgp);
|
listnode_delete(bm->bgp, bgp);
|
||||||
if (list_isempty(bm->bgp))
|
|
||||||
bgp_close();
|
|
||||||
|
|
||||||
/* Deregister from Zebra, if needed */
|
/* Deregister from Zebra, if needed */
|
||||||
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
|
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
|
||||||
@ -3199,6 +3282,7 @@ int bgp_delete(struct bgp *bgp)
|
|||||||
bgp_if_finish(bgp);
|
bgp_if_finish(bgp);
|
||||||
|
|
||||||
vrf = bgp_vrf_lookup_by_instance_type(bgp);
|
vrf = bgp_vrf_lookup_by_instance_type(bgp);
|
||||||
|
bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
|
||||||
if (vrf)
|
if (vrf)
|
||||||
bgp_vrf_unlink(bgp, vrf);
|
bgp_vrf_unlink(bgp, vrf);
|
||||||
|
|
||||||
@ -3337,11 +3421,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
|
|||||||
struct listnode *bgpnode, *nbgpnode;
|
struct listnode *bgpnode, *nbgpnode;
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
|
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
|
||||||
/* Skip VRFs, this function will not be invoked without
|
/* Skip VRFs Lite only, this function will not be
|
||||||
* an instance
|
* invoked without an instance
|
||||||
* when examining VRFs.
|
* 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;
|
continue;
|
||||||
|
|
||||||
peer = hash_lookup(bgp->peerhash, &tmp_peer);
|
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))
|
if (bgp_option_check(BGP_OPT_CONFIG_CISCO))
|
||||||
vty_out(vty, " no auto-summary\n");
|
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. */
|
/* IPv4 unicast configuration. */
|
||||||
bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
|
bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
|
||||||
|
|
||||||
|
@ -436,6 +436,7 @@ struct bgp {
|
|||||||
#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
|
#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
|
||||||
#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
|
#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
|
||||||
#define BGP_VRF_RD_CFGD (1 << 5)
|
#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 */
|
/* unique ID for auto derivation of RD for this vrf */
|
||||||
uint16_t vrf_rd_id;
|
uint16_t vrf_rd_id;
|
||||||
@ -452,6 +453,9 @@ struct bgp {
|
|||||||
/* list of corresponding l2vnis (struct bgpevpn) */
|
/* list of corresponding l2vnis (struct bgpevpn) */
|
||||||
struct list *l2vnis;
|
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
|
QOBJ_FIELDS
|
||||||
};
|
};
|
||||||
DECLARE_QOBJ_TYPE(bgp)
|
DECLARE_QOBJ_TYPE(bgp)
|
||||||
@ -683,7 +687,6 @@ struct peer {
|
|||||||
time_t readtime; /* Last read time */
|
time_t readtime; /* Last read time */
|
||||||
time_t resettime; /* Last reset time */
|
time_t resettime; /* Last reset time */
|
||||||
|
|
||||||
ifindex_t ifindex; /* ifindex of the BGP connection. */
|
|
||||||
char *conf_if; /* neighbor interface config name. */
|
char *conf_if; /* neighbor interface config name. */
|
||||||
struct interface *ifp; /* corresponding interface */
|
struct interface *ifp; /* corresponding interface */
|
||||||
char *ifname; /* bind interface name. */
|
char *ifname; /* bind interface name. */
|
||||||
@ -1351,6 +1354,9 @@ extern void bgp_instance_up(struct bgp *);
|
|||||||
extern void bgp_instance_down(struct bgp *);
|
extern void bgp_instance_down(struct bgp *);
|
||||||
extern int bgp_delete(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_set(struct bgp *, int);
|
||||||
extern int bgp_flag_unset(struct bgp *, int);
|
extern int bgp_flag_unset(struct bgp *, int);
|
||||||
extern int bgp_flag_check(struct bgp *, int);
|
extern int bgp_flag_check(struct bgp *, int);
|
||||||
|
@ -2977,6 +2977,11 @@ DEFUN_NOSH (vnc_vrf_policy,
|
|||||||
struct rfapi_nve_group_cfg *rfg;
|
struct rfapi_nve_group_cfg *rfg;
|
||||||
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
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 */
|
/* Search for name */
|
||||||
rfg = bgp_rfapi_cfg_match_byname(bgp, argv[1]->arg,
|
rfg = bgp_rfapi_cfg_match_byname(bgp, argv[1]->arg,
|
||||||
RFAPI_GROUP_CFG_VRF);
|
RFAPI_GROUP_CFG_VRF);
|
||||||
@ -3007,6 +3012,10 @@ DEFUN (vnc_no_vrf_policy,
|
|||||||
{
|
{
|
||||||
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
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,
|
return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[2]->arg,
|
||||||
RFAPI_GROUP_CFG_VRF);
|
RFAPI_GROUP_CFG_VRF);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
etc/frr/
|
etc/frr/
|
||||||
usr/bin/vtysh
|
usr/bin/vtysh
|
||||||
|
usr/bin/mtracebis
|
||||||
usr/include/frr/
|
usr/include/frr/
|
||||||
usr/lib/
|
usr/lib/
|
||||||
tools/frr etc/init.d/
|
tools/frr etc/init.d/
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
etc/frr/
|
etc/frr/
|
||||||
usr/bin/vtysh
|
usr/bin/vtysh
|
||||||
|
usr/bin/mtracebis
|
||||||
usr/include/frr/
|
usr/include/frr/
|
||||||
usr/lib/
|
usr/lib/
|
||||||
tools/frr usr/lib/frr
|
tools/frr usr/lib/frr
|
||||||
|
@ -11,6 +11,7 @@ man_MANS = $(MANPAGE_BUILDDIR)/frr.1
|
|||||||
|
|
||||||
if PIMD
|
if PIMD
|
||||||
man_MANS += $(MANPAGE_BUILDDIR)/pimd.8
|
man_MANS += $(MANPAGE_BUILDDIR)/pimd.8
|
||||||
|
man_MANS += $(MANPAGE_BUILDDIR)/mtracebis.8
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BGPD
|
if BGPD
|
||||||
@ -108,6 +109,7 @@ EXTRA_DIST = manpages/defines.rst \
|
|||||||
manpages/vtysh.rst \
|
manpages/vtysh.rst \
|
||||||
manpages/nhrpd.rst \
|
manpages/nhrpd.rst \
|
||||||
manpages/pimd.rst \
|
manpages/pimd.rst \
|
||||||
|
manpages/mtracebis.rst \
|
||||||
manpages/ripngd.rst \
|
manpages/ripngd.rst \
|
||||||
manpages/frr.rst \
|
manpages/frr.rst \
|
||||||
manpages/conf.py \
|
manpages/conf.py \
|
||||||
|
@ -22,7 +22,7 @@ Add packages:
|
|||||||
sudo yum install git autoconf automake libtool make gawk \
|
sudo yum install git autoconf automake libtool make gawk \
|
||||||
readline-devel texinfo net-snmp-devel groff pkgconfig \
|
readline-devel texinfo net-snmp-devel groff pkgconfig \
|
||||||
json-c-devel pam-devel bison flex pytest c-ares-devel \
|
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)
|
Get FRR, compile it and install it (from Git)
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
@ -68,6 +68,7 @@ an example.)
|
|||||||
--enable-group=frr \
|
--enable-group=frr \
|
||||||
--enable-vty-group=frrvt \
|
--enable-vty-group=frrvt \
|
||||||
--enable-rtadv \
|
--enable-rtadv \
|
||||||
|
--enable-systemd=yes \
|
||||||
--disable-exampledir \
|
--disable-exampledir \
|
||||||
--enable-watchfrr \
|
--enable-watchfrr \
|
||||||
--disable-ldpd \
|
--disable-ldpd \
|
||||||
|
@ -318,6 +318,7 @@ man_pages = [
|
|||||||
('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8),
|
('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8),
|
||||||
('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
|
('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
|
||||||
('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
|
('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
|
||||||
|
('mtracebis', 'mtracebis', "a multicast trace client", [], 8),
|
||||||
('ripd', 'ripd', fwfrr.format("a RIP "), [], 8),
|
('ripd', 'ripd', fwfrr.format("a RIP "), [], 8),
|
||||||
('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8),
|
('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8),
|
||||||
('zebra', 'zebra', 'a routing manager for use with associated FRRouting components.', [], 8),
|
('zebra', 'zebra', 'a routing manager for use with associated FRRouting components.', [], 8),
|
||||||
|
@ -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| 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]
|
.. |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)
|
||||||
|
@ -8,7 +8,7 @@ The daemon may log to standard output, to a VTY, to a log file, or through syslo
|
|||||||
|
|
||||||
SEE ALSO
|
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|
|
|PACKAGE_URL|
|
||||||
|
|
||||||
BUGS
|
BUGS
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
FRR
|
FRR
|
||||||
***
|
***
|
||||||
|
|
||||||
|
.. include:: defines.rst
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
========
|
========
|
||||||
frr [ start ]
|
frr [ start ]
|
||||||
|
@ -18,6 +18,7 @@ Welcome to FRR's documentation!
|
|||||||
ospfclient
|
ospfclient
|
||||||
ospfd
|
ospfd
|
||||||
pimd
|
pimd
|
||||||
|
mtracebis
|
||||||
ripd
|
ripd
|
||||||
ripngd
|
ripngd
|
||||||
watchfrr
|
watchfrr
|
||||||
|
39
doc/manpages/mtracebis.rst
Normal file
39
doc/manpages/mtracebis.rst
Normal 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
|
@ -2,6 +2,8 @@
|
|||||||
OSPFCLIENT
|
OSPFCLIENT
|
||||||
**********
|
**********
|
||||||
|
|
||||||
|
.. include:: defines.rst
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
========
|
========
|
||||||
ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>
|
ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>
|
||||||
|
@ -37,6 +37,11 @@ OPTIONS available for the |DAEMON| command:
|
|||||||
|
|
||||||
Note that this affects Linux only.
|
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
|
FILES
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
@ -94,13 +94,14 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int eigrp_zebra_notify_owner(int command, struct zclient *zclient,
|
static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient,
|
||||||
zebra_size_t length, vrf_id_t vrf_id)
|
zebra_size_t length, vrf_id_t vrf_id)
|
||||||
{
|
{
|
||||||
struct prefix p;
|
struct prefix p;
|
||||||
enum zapi_route_notify_owner note;
|
enum zapi_route_notify_owner note;
|
||||||
|
uint32_t table;
|
||||||
|
|
||||||
if (!zapi_route_notify_decode(zclient->ibuf, &p, ¬e))
|
if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -128,7 +129,7 @@ void eigrp_zebra_init(void)
|
|||||||
zclient->interface_address_delete = eigrp_interface_address_delete;
|
zclient->interface_address_delete = eigrp_interface_address_delete;
|
||||||
zclient->redistribute_route_add = eigrp_zebra_read_route;
|
zclient->redistribute_route_add = eigrp_zebra_read_route;
|
||||||
zclient->redistribute_route_del = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
23
include/linux/net_namespace.h
Normal file
23
include/linux/net_namespace.h
Normal 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_ */
|
@ -6,4 +6,5 @@ noinst_HEADERS += \
|
|||||||
include/linux/neighbour.h \
|
include/linux/neighbour.h \
|
||||||
include/linux/rtnetlink.h \
|
include/linux/rtnetlink.h \
|
||||||
include/linux/socket.h \
|
include/linux/socket.h \
|
||||||
|
include/linux/net_namespace.h \
|
||||||
# end
|
# end
|
||||||
|
@ -1168,7 +1168,6 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
|
|||||||
struct isis_area *area = circuit->area;
|
struct isis_area *area = circuit->area;
|
||||||
bool change = circuit->ip_router != ip_router
|
bool change = circuit->ip_router != ip_router
|
||||||
|| circuit->ipv6_router != ipv6_router;
|
|| circuit->ipv6_router != ipv6_router;
|
||||||
bool was_enabled = !!circuit->area;
|
|
||||||
|
|
||||||
area->ip_circuits += ip_router - circuit->ip_router;
|
area->ip_circuits += ip_router - circuit->ip_router;
|
||||||
area->ipv6_circuits += ipv6_router - circuit->ipv6_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)
|
if (!ip_router && !ipv6_router)
|
||||||
isis_csm_state_change(ISIS_DISABLE, circuit, area);
|
isis_csm_state_change(ISIS_DISABLE, circuit, area);
|
||||||
else if (!was_enabled)
|
|
||||||
isis_csm_state_change(ISIS_ENABLE, circuit, area);
|
|
||||||
else
|
else
|
||||||
lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
|
lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
|
||||||
}
|
}
|
||||||
|
@ -306,8 +306,11 @@ if_reset(struct iface *iface, int af)
|
|||||||
ia = iface_af_get(iface, af);
|
ia = iface_af_get(iface, af);
|
||||||
if_stop_hello_timer(ia);
|
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);
|
adj_del(adj, S_SHUTDOWN);
|
||||||
|
}
|
||||||
|
|
||||||
/* try to cleanup */
|
/* try to cleanup */
|
||||||
switch (af) {
|
switch (af) {
|
||||||
|
13
ldpd/l2vpn.c
13
ldpd/l2vpn.c
@ -76,16 +76,21 @@ l2vpn_del(struct l2vpn *l2vpn)
|
|||||||
struct l2vpn_if *lif;
|
struct l2vpn_if *lif;
|
||||||
struct l2vpn_pw *pw;
|
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);
|
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
|
||||||
free(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);
|
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
|
||||||
free(pw);
|
free(pw);
|
||||||
}
|
}
|
||||||
while ((pw = RB_ROOT(l2vpn_pw_head,
|
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
|
||||||
&l2vpn->pw_inactive_tree)) != NULL) {
|
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
|
||||||
|
|
||||||
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
|
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
|
||||||
free(pw);
|
free(pw);
|
||||||
}
|
}
|
||||||
|
@ -1324,8 +1324,11 @@ lde_nbr_clear(void)
|
|||||||
{
|
{
|
||||||
struct lde_nbr *ln;
|
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);
|
lde_nbr_del(ln);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -129,7 +129,9 @@ fec_clear(struct fec_tree *fh, void (*free_cb)(void *))
|
|||||||
{
|
{
|
||||||
struct fec *f;
|
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);
|
fec_remove(fh, f);
|
||||||
free_cb(f);
|
free_cb(f);
|
||||||
}
|
}
|
||||||
|
@ -1475,18 +1475,23 @@ l2vpn_del_api(struct ldpd_conf *conf, struct l2vpn *l2vpn)
|
|||||||
struct l2vpn_if *lif;
|
struct l2vpn_if *lif;
|
||||||
struct l2vpn_pw *pw;
|
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);
|
QOBJ_UNREG(lif);
|
||||||
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
|
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
|
||||||
free(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);
|
QOBJ_UNREG(pw);
|
||||||
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
|
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
|
||||||
free(pw);
|
free(pw);
|
||||||
}
|
}
|
||||||
while ((pw = RB_ROOT(l2vpn_pw_head,
|
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
|
||||||
&l2vpn->pw_inactive_tree)) != NULL) {
|
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
|
||||||
|
|
||||||
QOBJ_UNREG(pw);
|
QOBJ_UNREG(pw);
|
||||||
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
|
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
|
||||||
free(pw);
|
free(pw);
|
||||||
|
41
ldpd/ldpd.c
41
ldpd/ldpd.c
@ -1066,13 +1066,17 @@ ldp_config_reset_main(struct ldpd_conf *conf)
|
|||||||
struct iface *iface;
|
struct iface *iface;
|
||||||
struct nbr_params *nbrp;
|
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);
|
QOBJ_UNREG(iface);
|
||||||
RB_REMOVE(iface_head, &conf->iface_tree, iface);
|
RB_REMOVE(iface_head, &conf->iface_tree, iface);
|
||||||
free(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);
|
QOBJ_UNREG(nbrp);
|
||||||
RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
|
RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
|
||||||
free(nbrp);
|
free(nbrp);
|
||||||
@ -1128,20 +1132,25 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf)
|
|||||||
struct l2vpn_if *lif;
|
struct l2vpn_if *lif;
|
||||||
struct l2vpn_pw *pw;
|
struct l2vpn_pw *pw;
|
||||||
|
|
||||||
while ((l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree)) != NULL) {
|
while (!RB_EMPTY(l2vpn_head, &conf->l2vpn_tree)) {
|
||||||
while ((lif = RB_ROOT(l2vpn_if_head,
|
l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree);
|
||||||
&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);
|
QOBJ_UNREG(lif);
|
||||||
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
|
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
|
||||||
free(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);
|
QOBJ_UNREG(pw);
|
||||||
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
|
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
|
||||||
free(pw);
|
free(pw);
|
||||||
}
|
}
|
||||||
while ((pw = RB_ROOT(l2vpn_pw_head,
|
while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
|
||||||
&l2vpn->pw_inactive_tree)) != NULL) {
|
pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
|
||||||
|
|
||||||
QOBJ_UNREG(pw);
|
QOBJ_UNREG(pw);
|
||||||
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
|
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
|
||||||
free(pw);
|
free(pw);
|
||||||
@ -1160,19 +1169,27 @@ ldp_clear_config(struct ldpd_conf *xconf)
|
|||||||
struct nbr_params *nbrp;
|
struct nbr_params *nbrp;
|
||||||
struct l2vpn *l2vpn;
|
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);
|
RB_REMOVE(iface_head, &xconf->iface_tree, iface);
|
||||||
free(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);
|
RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr);
|
||||||
free(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);
|
RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp);
|
||||||
free(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);
|
RB_REMOVE(l2vpn_head, &xconf->l2vpn_tree, l2vpn);
|
||||||
l2vpn_del(l2vpn);
|
l2vpn_del(l2vpn);
|
||||||
}
|
}
|
||||||
|
@ -219,8 +219,11 @@ ldpe_shutdown(void)
|
|||||||
assert(if_addr != LIST_FIRST(&global.addr_list));
|
assert(if_addr != LIST_FIRST(&global.addr_list));
|
||||||
free(if_addr);
|
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);
|
adj_del(adj, S_SHUTDOWN);
|
||||||
|
}
|
||||||
|
|
||||||
/* clean up */
|
/* clean up */
|
||||||
if (iev_lde)
|
if (iev_lde)
|
||||||
|
@ -316,7 +316,9 @@ nbr_del(struct nbr *nbr)
|
|||||||
mapping_list_clr(&nbr->release_list);
|
mapping_list_clr(&nbr->release_list);
|
||||||
mapping_list_clr(&nbr->abortreq_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;
|
adj->nbr = NULL;
|
||||||
RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
|
RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ const char *node_names[] = {
|
|||||||
"aaa", // AAA_NODE,
|
"aaa", // AAA_NODE,
|
||||||
"keychain", // KEYCHAIN_NODE,
|
"keychain", // KEYCHAIN_NODE,
|
||||||
"keychain key", // KEYCHAIN_KEY_NODE,
|
"keychain key", // KEYCHAIN_KEY_NODE,
|
||||||
"logical-router", // NS_NODE,
|
"logical-router", // LOGICALROUTER_NODE,
|
||||||
"vrf", // VRF_NODE,
|
"vrf", // VRF_NODE,
|
||||||
"interface", // INTERFACE_NODE,
|
"interface", // INTERFACE_NODE,
|
||||||
"zebra", // ZEBRA_NODE,
|
"zebra", // ZEBRA_NODE,
|
||||||
@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty)
|
|||||||
break;
|
break;
|
||||||
case INTERFACE_NODE:
|
case INTERFACE_NODE:
|
||||||
case PW_NODE:
|
case PW_NODE:
|
||||||
case NS_NODE:
|
case LOGICALROUTER_NODE:
|
||||||
case VRF_NODE:
|
case VRF_NODE:
|
||||||
case ZEBRA_NODE:
|
case ZEBRA_NODE:
|
||||||
case BGP_NODE:
|
case BGP_NODE:
|
||||||
@ -1376,7 +1376,7 @@ DEFUN (config_end,
|
|||||||
case CONFIG_NODE:
|
case CONFIG_NODE:
|
||||||
case INTERFACE_NODE:
|
case INTERFACE_NODE:
|
||||||
case PW_NODE:
|
case PW_NODE:
|
||||||
case NS_NODE:
|
case LOGICALROUTER_NODE:
|
||||||
case VRF_NODE:
|
case VRF_NODE:
|
||||||
case ZEBRA_NODE:
|
case ZEBRA_NODE:
|
||||||
case RIP_NODE:
|
case RIP_NODE:
|
||||||
|
@ -85,7 +85,7 @@ enum node_type {
|
|||||||
AAA_NODE, /* AAA node. */
|
AAA_NODE, /* AAA node. */
|
||||||
KEYCHAIN_NODE, /* Key-chain node. */
|
KEYCHAIN_NODE, /* Key-chain node. */
|
||||||
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
|
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
|
||||||
NS_NODE, /* Logical-Router node. */
|
LOGICALROUTER_NODE, /* Logical-Router node. */
|
||||||
VRF_NODE, /* VRF mode node. */
|
VRF_NODE, /* VRF mode node. */
|
||||||
INTERFACE_NODE, /* Interface mode node. */
|
INTERFACE_NODE, /* Interface mode node. */
|
||||||
ZEBRA_NODE, /* zebra connection node. */
|
ZEBRA_NODE, /* zebra connection node. */
|
||||||
|
36
lib/if.c
36
lib/if.c
@ -384,29 +384,35 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty)
|
|||||||
{
|
{
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
|
|
||||||
ifp = if_lookup_by_name_all_vrf(name);
|
ifp = if_lookup_by_name(name, vrf_id);
|
||||||
if (ifp) {
|
if (ifp)
|
||||||
if (ifp->vrf_id == vrf_id)
|
|
||||||
return ifp;
|
return ifp;
|
||||||
|
/* Not Found on same VRF. If the interface command
|
||||||
/* Found a match on a different VRF. If the interface command
|
|
||||||
* was entered in vty without a VRF (passed as VRF_DEFAULT),
|
* was entered in vty without a VRF (passed as VRF_DEFAULT),
|
||||||
* accept the ifp we found. If a vrf was entered and there is
|
* 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
|
* a mismatch, reject it if from vty.
|
||||||
* or by way of zclient, believe it and update the ifp
|
|
||||||
* accordingly.
|
|
||||||
*/
|
*/
|
||||||
|
ifp = if_lookup_by_name_all_vrf(name);
|
||||||
|
if (!ifp)
|
||||||
|
return if_create(name, vrf_id);
|
||||||
if (vty) {
|
if (vty) {
|
||||||
if (vrf_id == VRF_DEFAULT)
|
if (vrf_id == VRF_DEFAULT)
|
||||||
return ifp;
|
return ifp;
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
}
|
||||||
|
/* 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);
|
if_update_to_new_vrf(ifp, vrf_id);
|
||||||
return ifp;
|
return ifp;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return if_create(name, vrf_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void if_set_index(struct interface *ifp, ifindex_t ifindex)
|
void if_set_index(struct interface *ifp, ifindex_t ifindex)
|
||||||
@ -1078,7 +1084,9 @@ void if_terminate(struct vrf *vrf)
|
|||||||
{
|
{
|
||||||
struct interface *ifp;
|
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) {
|
if (ifp->node) {
|
||||||
ifp->node->info = NULL;
|
ifp->node->info = NULL;
|
||||||
route_unlock_node(ifp->node);
|
route_unlock_node(ifp->node);
|
||||||
|
159
lib/logicalrouter.c
Normal file
159
lib/logicalrouter.c
Normal 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
41
lib/logicalrouter.h
Normal 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
539
lib/netns_linux.c
Normal 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
165
lib/netns_other.c
Normal 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
453
lib/ns.c
@ -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;
|
|
||||||
}
|
|
88
lib/ns.h
88
lib/ns.h
@ -24,16 +24,22 @@
|
|||||||
|
|
||||||
#include "openbsd-tree.h"
|
#include "openbsd-tree.h"
|
||||||
#include "linklist.h"
|
#include "linklist.h"
|
||||||
|
#include "vty.h"
|
||||||
|
|
||||||
typedef u_int32_t ns_id_t;
|
typedef u_int32_t ns_id_t;
|
||||||
|
|
||||||
/* the default NS ID */
|
/* the default NS ID */
|
||||||
#define NS_DEFAULT 0
|
|
||||||
#define NS_UNKNOWN UINT32_MAX
|
#define NS_UNKNOWN UINT32_MAX
|
||||||
|
|
||||||
/* Default netns directory (Linux) */
|
/* Default netns directory (Linux) */
|
||||||
#define NS_RUN_DIR "/var/run/netns"
|
#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 {
|
struct ns {
|
||||||
RB_ENTRY(ns) entry;
|
RB_ENTRY(ns) entry;
|
||||||
|
|
||||||
@ -49,6 +55,9 @@ struct ns {
|
|||||||
/* Master list of interfaces belonging to this NS */
|
/* Master list of interfaces belonging to this NS */
|
||||||
struct list *iflist;
|
struct list *iflist;
|
||||||
|
|
||||||
|
/* Back Pointer to VRF */
|
||||||
|
void *vrf_ctxt;
|
||||||
|
|
||||||
/* User data */
|
/* User data */
|
||||||
void *info;
|
void *info;
|
||||||
};
|
};
|
||||||
@ -57,6 +66,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare)
|
|||||||
|
|
||||||
extern struct ns_head ns_tree;
|
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
|
* NS hooks
|
||||||
*/
|
*/
|
||||||
@ -74,20 +88,82 @@ extern struct ns_head ns_tree;
|
|||||||
* - param 2: the address of the user data pointer (the user data
|
* - param 2: the address of the user data pointer (the user data
|
||||||
* can be stored in or freed from there)
|
* 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
|
* NS initializer/destructor
|
||||||
*/
|
*/
|
||||||
/* Please add hooks before calling ns_init(). */
|
|
||||||
extern void ns_init(void);
|
|
||||||
extern void ns_terminate(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
|
* NS utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Create a socket serving for the given NS */
|
/* Create a socket serving for the given NS
|
||||||
extern int ns_socket(int, int, int, ns_id_t);
|
*/
|
||||||
|
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*/
|
#endif /*_ZEBRA_NS_H*/
|
||||||
|
@ -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_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
|
||||||
#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
|
#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
|
||||||
#define IPV4_CLASS_DE(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
|
#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. */
|
/* Max bit/byte length of IPv6 address. */
|
||||||
#define IPV6_MAX_BYTELEN 16
|
#define IPV6_MAX_BYTELEN 16
|
||||||
|
@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \
|
|||||||
lib/module.c \
|
lib/module.c \
|
||||||
lib/network.c \
|
lib/network.c \
|
||||||
lib/nexthop.c \
|
lib/nexthop.c \
|
||||||
lib/ns.c \
|
lib/netns_linux.c \
|
||||||
|
lib/netns_other.c \
|
||||||
lib/openbsd-tree.c \
|
lib/openbsd-tree.c \
|
||||||
lib/pid_output.c \
|
lib/pid_output.c \
|
||||||
lib/plist.c \
|
lib/plist.c \
|
||||||
@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \
|
|||||||
lib/wheel.c \
|
lib/wheel.c \
|
||||||
lib/workqueue.c \
|
lib/workqueue.c \
|
||||||
lib/zclient.c \
|
lib/zclient.c \
|
||||||
|
lib/logicalrouter.c \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
lib/plist_clippy.c: $(CLIPPY_DEPS)
|
lib/plist_clippy.c: $(CLIPPY_DEPS)
|
||||||
@ -158,6 +160,7 @@ pkginclude_HEADERS += \
|
|||||||
lib/zassert.h \
|
lib/zassert.h \
|
||||||
lib/zclient.h \
|
lib/zclient.h \
|
||||||
lib/zebra.h \
|
lib/zebra.h \
|
||||||
|
lib/logicalrouter.h \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
nodist_pkginclude_HEADERS += \
|
nodist_pkginclude_HEADERS += \
|
||||||
|
383
lib/vrf.c
383
lib/vrf.c
@ -21,6 +21,9 @@
|
|||||||
|
|
||||||
#include <zebra.h>
|
#include <zebra.h>
|
||||||
|
|
||||||
|
/* for basename */
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "if.h"
|
#include "if.h"
|
||||||
#include "vrf.h"
|
#include "vrf.h"
|
||||||
#include "vrf_int.h"
|
#include "vrf_int.h"
|
||||||
@ -29,6 +32,10 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "command.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, "VRF")
|
||||||
DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map")
|
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_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id);
|
||||||
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
|
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
|
||||||
|
|
||||||
|
static int vrf_backend;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Turn on/off debug code
|
* Turn on/off debug code
|
||||||
* for vrf.
|
* for vrf.
|
||||||
@ -61,7 +70,6 @@ struct vrf_master {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int vrf_is_enabled(struct vrf *vrf);
|
static int vrf_is_enabled(struct vrf *vrf);
|
||||||
static void vrf_disable(struct vrf *vrf);
|
|
||||||
|
|
||||||
/* VRF list existance check by name. */
|
/* VRF list existance check by name. */
|
||||||
struct vrf *vrf_lookup_by_name(const char *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);
|
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.
|
/* Get a VRF. If not found, create one.
|
||||||
* Arg:
|
* Arg:
|
||||||
* name - The name of the vrf. May be NULL if unknown.
|
* 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;
|
int new = 0;
|
||||||
|
|
||||||
if (debug_vrf)
|
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 */
|
/* Nothing to see, move along here */
|
||||||
if (!name && vrf_id == VRF_UNKNOWN)
|
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) {
|
if (vrf == NULL) {
|
||||||
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
|
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
|
||||||
vrf->vrf_id = VRF_UNKNOWN;
|
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);
|
QOBJ_REG(vrf, vrf);
|
||||||
new = 1;
|
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));
|
strlcpy(vrf->name, name, sizeof(vrf->name));
|
||||||
RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
|
RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new &&vrf_master.vrf_new_hook)
|
if (new &&vrf_master.vrf_new_hook)
|
||||||
(*vrf_master.vrf_new_hook)(vrf);
|
(*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
|
* The VRF_DELETE_HOOK callback will be called to inform
|
||||||
* that they must release the resources in the VRF.
|
* 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))
|
if (!vrf_is_enabled(vrf))
|
||||||
return;
|
return;
|
||||||
@ -385,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
|
|||||||
{
|
{
|
||||||
struct vrf *default_vrf;
|
struct vrf *default_vrf;
|
||||||
|
|
||||||
|
/* initialise NS, in case VRF backend if NETNS */
|
||||||
|
ns_init();
|
||||||
if (debug_vrf)
|
if (debug_vrf)
|
||||||
zlog_debug("%s: Initializing VRF subsystem",
|
zlog_debug("%s: Initializing VRF subsystem",
|
||||||
__PRETTY_FUNCTION__);
|
__PRETTY_FUNCTION__);
|
||||||
@ -419,12 +475,17 @@ void vrf_terminate(void)
|
|||||||
zlog_debug("%s: Shutting down vrf subsystem",
|
zlog_debug("%s: Shutting down vrf subsystem",
|
||||||
__PRETTY_FUNCTION__);
|
__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. */
|
/* Clear configured flag and invoke delete. */
|
||||||
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
|
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
|
||||||
vrf_delete(vrf);
|
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. */
|
/* Clear configured flag and invoke delete. */
|
||||||
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
|
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
|
||||||
vrf_delete(vrf);
|
vrf_delete(vrf);
|
||||||
@ -432,13 +493,148 @@ void vrf_terminate(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create a socket for the VRF. */
|
/* 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);
|
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;
|
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 */
|
/* vrf CLI commands */
|
||||||
@ -450,21 +646,8 @@ DEFUN_NOSH (vrf,
|
|||||||
{
|
{
|
||||||
int idx_name = 1;
|
int idx_name = 1;
|
||||||
const char *vrfname = argv[idx_name]->arg;
|
const char *vrfname = argv[idx_name]->arg;
|
||||||
struct vrf *vrfp;
|
|
||||||
|
|
||||||
if (strlen(vrfname) > VRF_NAMSIZ) {
|
return vrf_handler_create(vty, vrfname, NULL);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN_NOSH (no_vrf,
|
DEFUN_NOSH (no_vrf,
|
||||||
@ -500,6 +683,55 @@ DEFUN_NOSH (no_vrf,
|
|||||||
|
|
||||||
struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
|
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
|
* 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_element(CONFIG_NODE, &no_vrf_cmd);
|
||||||
install_node(&vrf_node, writefunc);
|
install_node(&vrf_node, writefunc);
|
||||||
install_default(VRF_NODE);
|
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;
|
||||||
}
|
}
|
||||||
|
93
lib/vrf.h
93
lib/vrf.h
@ -26,12 +26,9 @@
|
|||||||
#include "linklist.h"
|
#include "linklist.h"
|
||||||
#include "qobj.h"
|
#include "qobj.h"
|
||||||
#include "vty.h"
|
#include "vty.h"
|
||||||
|
#include "ns.h"
|
||||||
/* The default NS ID */
|
|
||||||
#define NS_DEFAULT 0
|
|
||||||
|
|
||||||
/* The default VRF ID */
|
/* The default VRF ID */
|
||||||
#define VRF_DEFAULT 0
|
|
||||||
#define VRF_UNKNOWN UINT32_MAX
|
#define VRF_UNKNOWN UINT32_MAX
|
||||||
|
|
||||||
/* Pending: May need to refine this. */
|
/* Pending: May need to refine this. */
|
||||||
@ -42,6 +39,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX };
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VRF_NAMSIZ 36
|
#define VRF_NAMSIZ 36
|
||||||
|
#define NS_NAMSIZ 16
|
||||||
|
|
||||||
#define VRF_DEFAULT_NAME "Default-IP-Routing-Table"
|
#define VRF_DEFAULT_NAME "Default-IP-Routing-Table"
|
||||||
|
|
||||||
@ -60,6 +58,7 @@ struct vrf_data {
|
|||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t table_id;
|
uint32_t table_id;
|
||||||
|
char netns_name[NS_NAMSIZ];
|
||||||
} l;
|
} l;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -88,6 +87,9 @@ struct vrf {
|
|||||||
/* The table_id from the kernel */
|
/* The table_id from the kernel */
|
||||||
struct vrf_data data;
|
struct vrf_data data;
|
||||||
|
|
||||||
|
/* Back pointer to namespace context */
|
||||||
|
void *ns_ctxt;
|
||||||
|
|
||||||
QOBJ_FIELDS
|
QOBJ_FIELDS
|
||||||
};
|
};
|
||||||
RB_HEAD(vrf_id_head, vrf);
|
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)
|
RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
|
||||||
DECLARE_QOBJ_TYPE(vrf)
|
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_id_head vrfs_by_id;
|
||||||
extern struct vrf_name_head vrfs_by_name;
|
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);
|
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));
|
extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
|
||||||
|
|
||||||
|
/* VRF vty debugging
|
||||||
|
*/
|
||||||
|
extern void vrf_install_commands(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* VRF utilities
|
* VRF utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Create a socket serving for the given VRF */
|
/* API for configuring VRF backend
|
||||||
extern int vrf_socket(int, int, int, vrf_id_t);
|
* should be called from zebra only
|
||||||
|
|
||||||
/*
|
|
||||||
* VRF Debugging
|
|
||||||
*/
|
*/
|
||||||
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*/
|
#endif /*_ZEBRA_VRF_H*/
|
||||||
|
@ -363,7 +363,7 @@ static int zebra_hello_send(struct zclient *zclient)
|
|||||||
return 0;
|
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)
|
mpls_label_t label, enum lsp_types_t ltype)
|
||||||
{
|
{
|
||||||
struct stream *s;
|
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);
|
zclient_create_header(s, ZEBRA_VRF_LABEL, vrf_id);
|
||||||
stream_putl(s, label);
|
stream_putl(s, label);
|
||||||
|
stream_putc(s, afi);
|
||||||
stream_putc(s, ltype);
|
stream_putc(s, ltype);
|
||||||
stream_putw_at(s, 0, stream_get_endp(s));
|
stream_putw_at(s, 0, stream_get_endp(s));
|
||||||
zclient_send_message(zclient);
|
zclient_send_message(zclient);
|
||||||
@ -1209,14 +1210,20 @@ stream_failure:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
|
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
|
||||||
|
uint32_t *tableid,
|
||||||
enum zapi_route_notify_owner *note)
|
enum zapi_route_notify_owner *note)
|
||||||
{
|
{
|
||||||
|
uint32_t t;
|
||||||
|
|
||||||
STREAM_GET(note, s, sizeof(*note));
|
STREAM_GET(note, s, sizeof(*note));
|
||||||
|
|
||||||
STREAM_GETC(s, p->family);
|
STREAM_GETC(s, p->family);
|
||||||
STREAM_GETC(s, p->prefixlen);
|
STREAM_GETC(s, p->prefixlen);
|
||||||
STREAM_GET(&p->u.prefix, s,
|
STREAM_GET(&p->u.prefix, s,
|
||||||
PSIZE(p->prefixlen));
|
prefix_blen(p));
|
||||||
|
STREAM_GETL(s, t);
|
||||||
|
|
||||||
|
*tableid = t;
|
||||||
|
|
||||||
return true;
|
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. */
|
/* Lookup/create vrf by vrf_id. */
|
||||||
vrf = vrf_get(vrf_id, vrfname_tmp);
|
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);
|
vrf_enable(vrf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2362,9 +2369,9 @@ static int zclient_read(struct thread *thread)
|
|||||||
vrf_id);
|
vrf_id);
|
||||||
break;
|
break;
|
||||||
case ZEBRA_ROUTE_NOTIFY_OWNER:
|
case ZEBRA_ROUTE_NOTIFY_OWNER:
|
||||||
if (zclient->notify_owner)
|
if (zclient->route_notify_owner)
|
||||||
(*zclient->notify_owner)(command, zclient,
|
(*zclient->route_notify_owner)(command, zclient, length,
|
||||||
length, vrf_id);
|
vrf_id);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -213,7 +213,7 @@ struct zclient {
|
|||||||
int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
|
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 (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
|
||||||
int (*pw_status_update)(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,
|
int (*route_notify_owner)(int command, struct zclient *zclient,
|
||||||
uint16_t length, vrf_id_t vrf_id);
|
uint16_t length, vrf_id_t vrf_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -390,9 +390,15 @@ extern void redist_del_instance(struct redist_proto *, u_short);
|
|||||||
* label for lookup. If you pass in MPLS_LABEL_NONE
|
* label for lookup. If you pass in MPLS_LABEL_NONE
|
||||||
* we will cause a delete action and remove this label pop
|
* we will cause a delete action and remove this label pop
|
||||||
* operation.
|
* 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,
|
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_reg_requests(struct zclient *, vrf_id_t);
|
||||||
extern void zclient_send_dereg_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_encode(u_char, struct stream *, struct zapi_route *);
|
||||||
extern int zapi_route_decode(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,
|
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
|
||||||
|
uint32_t *tableid,
|
||||||
enum zapi_route_notify_owner *note);
|
enum zapi_route_notify_owner *note);
|
||||||
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
|
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
|
||||||
extern bool zapi_nexthop_update_decode(struct stream *s,
|
extern bool zapi_nexthop_update_decode(struct stream *s,
|
||||||
|
@ -402,7 +402,7 @@ extern const char *zserv_command_string(unsigned int command);
|
|||||||
#define strmatch(a,b) (!strcmp((a), (b)))
|
#define strmatch(a,b) (!strcmp((a), (b)))
|
||||||
|
|
||||||
/* Zebra message flags */
|
/* Zebra message flags */
|
||||||
#define ZEBRA_FLAG_INTERNAL 0x01
|
#define ZEBRA_FLAG_ALLOW_RECURSION 0x01
|
||||||
#define ZEBRA_FLAG_SELFROUTE 0x02
|
#define ZEBRA_FLAG_SELFROUTE 0x02
|
||||||
#define ZEBRA_FLAG_IBGP 0x08
|
#define ZEBRA_FLAG_IBGP 0x08
|
||||||
#define ZEBRA_FLAG_SELECTED 0x10
|
#define ZEBRA_FLAG_SELECTED 0x10
|
||||||
|
@ -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);
|
SET_FLAG(api.flags, ZEBRA_FLAG_FIB_OVERRIDE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
|
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
|
||||||
|
|
||||||
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
||||||
api.nexthop_num = 1;
|
api.nexthop_num = 1;
|
||||||
|
@ -99,6 +99,8 @@ struct ospf6_area {
|
|||||||
|
|
||||||
/* Time stamps. */
|
/* Time stamps. */
|
||||||
struct timeval ts_spf; /* SPF calculation time stamp. */
|
struct timeval ts_spf; /* SPF calculation time stamp. */
|
||||||
|
|
||||||
|
uint32_t full_nbrs; /* Fully adjacent neighbors. */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define OSPF6_AREA_ENABLE 0x01
|
#define OSPF6_AREA_ENABLE 0x01
|
||||||
|
@ -193,7 +193,11 @@ static void ospf6_neighbor_state_change(u_char next_state,
|
|||||||
if (prev_state == OSPF6_NEIGHBOR_LOADING &&
|
if (prev_state == OSPF6_NEIGHBOR_LOADING &&
|
||||||
next_state == OSPF6_NEIGHBOR_FULL) {
|
next_state == OSPF6_NEIGHBOR_FULL) {
|
||||||
OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if);
|
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
|
if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE
|
||||||
|
@ -1029,19 +1029,22 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area,
|
|||||||
|
|
||||||
/* Fill Larger LSA Payload */
|
/* Fill Larger LSA Payload */
|
||||||
end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa);
|
end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa);
|
||||||
if (rtr_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)) {
|
if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
|
||||||
/* Append first Link State ID LSA */
|
/* Append first Link State ID LSA */
|
||||||
lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header;
|
lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header;
|
||||||
memcpy(new_header, lsa_header,
|
memcpy(new_header, lsa_header, ntohs(lsa_header->length));
|
||||||
ntohs(lsa_header->length));
|
|
||||||
/* Assign new lsa length as aggregated length. */
|
/* Assign new lsa length as aggregated length. */
|
||||||
((struct ospf6_lsa_header *)new_header)->length =
|
((struct ospf6_lsa_header *)new_header)->length =
|
||||||
htons(total_lsa_length);
|
htons(total_lsa_length);
|
||||||
new_header += ntohs(lsa_header->length);
|
new_header += ntohs(lsa_header->length);
|
||||||
num_lsa--;
|
num_lsa--;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Print LSA Name */
|
/* Print LSA Name */
|
||||||
ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name));
|
ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name));
|
||||||
|
@ -335,6 +335,8 @@ DEFUN(ospf6_router_id,
|
|||||||
int ret;
|
int ret;
|
||||||
const char *router_id_str;
|
const char *router_id_str;
|
||||||
u_int32_t router_id;
|
u_int32_t router_id;
|
||||||
|
struct ospf6_area *oa;
|
||||||
|
struct listnode *node;
|
||||||
|
|
||||||
argv_find(argv, argc, "A.B.C.D", &idx);
|
argv_find(argv, argc, "A.B.C.D", &idx);
|
||||||
router_id_str = argv[idx]->arg;
|
router_id_str = argv[idx]->arg;
|
||||||
@ -346,7 +348,16 @@ DEFUN(ospf6_router_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
o->router_id_static = router_id;
|
o->router_id_static = router_id;
|
||||||
if (o->router_id == 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 = router_id;
|
o->router_id = router_id;
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
@ -360,8 +371,22 @@ DEFUN(no_ospf6_router_id,
|
|||||||
V4NOTATION_STR)
|
V4NOTATION_STR)
|
||||||
{
|
{
|
||||||
VTY_DECLVAR_CONTEXT(ospf6, o);
|
VTY_DECLVAR_CONTEXT(ospf6, o);
|
||||||
|
struct ospf6_area *oa;
|
||||||
|
struct listnode *node;
|
||||||
|
|
||||||
o->router_id_static = 0;
|
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;
|
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;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -523,6 +548,10 @@ DEFUN (ospf6_distance_ospf6,
|
|||||||
VTY_DECLVAR_CONTEXT(ospf6, o);
|
VTY_DECLVAR_CONTEXT(ospf6, o);
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
|
o->distance_intra = 0;
|
||||||
|
o->distance_inter = 0;
|
||||||
|
o->distance_external = 0;
|
||||||
|
|
||||||
if (argv_find(argv, argc, "intra-area", &idx))
|
if (argv_find(argv, argc, "intra-area", &idx))
|
||||||
o->distance_intra = atoi(argv[idx + 1]->arg);
|
o->distance_intra = atoi(argv[idx + 1]->arg);
|
||||||
idx = 0;
|
idx = 0;
|
||||||
|
@ -32,6 +32,8 @@ struct ospf6 {
|
|||||||
/* static router id */
|
/* static router id */
|
||||||
u_int32_t router_id_static;
|
u_int32_t router_id_static;
|
||||||
|
|
||||||
|
struct in_addr router_id_zebra;
|
||||||
|
|
||||||
/* start time */
|
/* start time */
|
||||||
struct timeval starttime;
|
struct timeval starttime;
|
||||||
|
|
||||||
|
@ -46,8 +46,6 @@ unsigned char conf_debug_ospf6_zebra = 0;
|
|||||||
/* information about zebra. */
|
/* information about zebra. */
|
||||||
struct zclient *zclient = NULL;
|
struct zclient *zclient = NULL;
|
||||||
|
|
||||||
struct in_addr router_id_zebra;
|
|
||||||
|
|
||||||
/* Router-id update message from zebra. */
|
/* Router-id update message from zebra. */
|
||||||
static int ospf6_router_id_update_zebra(int command, struct zclient *zclient,
|
static int ospf6_router_id_update_zebra(int command, struct zclient *zclient,
|
||||||
zebra_size_t length, vrf_id_t vrf_id)
|
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;
|
struct ospf6 *o = ospf6;
|
||||||
|
|
||||||
zebra_router_id_update_read(zclient->ibuf, &router_id);
|
zebra_router_id_update_read(zclient->ibuf, &router_id);
|
||||||
router_id_zebra = router_id.u.prefix4;
|
|
||||||
|
|
||||||
if (o == NULL)
|
if (o == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
o->router_id_zebra = router_id.u.prefix4;
|
||||||
|
|
||||||
if (o->router_id == 0)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ void ospf_ext_term(void)
|
|||||||
{
|
{
|
||||||
|
|
||||||
if ((OspfEXT.scope != OSPF_OPAQUE_AREA_LSA)
|
if ((OspfEXT.scope != OSPF_OPAQUE_AREA_LSA)
|
||||||
|| (OspfEXT.scope != OSPF_OPAQUE_AS_LSA))
|
&& (OspfEXT.scope != OSPF_OPAQUE_AS_LSA))
|
||||||
zlog_warn(
|
zlog_warn(
|
||||||
"EXT: Unable to unregister Extended Prefix "
|
"EXT: Unable to unregister Extended Prefix "
|
||||||
"Opaque LSA functions: Wrong scope!");
|
"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 */
|
/* Set Label or Index value */
|
||||||
if (value_type == SID_LABEL) {
|
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));
|
exti->node_sid.value = htonl(SET_LABEL(value));
|
||||||
} else {
|
} 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);
|
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 */
|
/* Adjust Length, Flags and Value depending on the type of Label */
|
||||||
if (value_type == SID_LABEL) {
|
if (value_type == SID_LABEL) {
|
||||||
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
|
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));
|
exti->adj_sid[index].value = htonl(SET_LABEL(value));
|
||||||
} else {
|
} else {
|
||||||
UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
|
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);
|
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 */
|
/* 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 */
|
/* Only Local ADJ-SID is supported for the moment */
|
||||||
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
|
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 */
|
/* Adjust Length, Flags and Value depending on the type of Label */
|
||||||
if (value_type == SID_LABEL) {
|
if (value_type == SID_LABEL) {
|
||||||
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
|
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));
|
exti->lan_sid[index].value = htonl(SET_LABEL(value));
|
||||||
} else {
|
} else {
|
||||||
UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
|
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);
|
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 LSA body for an Extended Link TLV with Adj. SID */
|
||||||
build_tlv_header(s, &exti->link.header);
|
build_tlv_header(s, &exti->link.header);
|
||||||
stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE);
|
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[1].header);
|
||||||
build_tlv(s, &exti->adj_sid[0].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 LSA body for an Extended Link TLV with LAN SID */
|
||||||
build_tlv_header(s, &exti->link.header);
|
build_tlv_header(s, &exti->link.header);
|
||||||
stream_put(s, &exti->link.header, EXT_TLV_LINK_SIZE);
|
stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE);
|
||||||
/* then add LAN-Ajacency SubTLVs */
|
/* then add LAN-Adjacency SubTLVs */
|
||||||
build_tlv(s, &exti->lan_sid[1].header);
|
build_tlv(s, &exti->lan_sid[1].header);
|
||||||
build_tlv(s, &exti->lan_sid[0].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,
|
vty_out(vty,
|
||||||
" LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: "
|
" LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: "
|
||||||
"0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: "
|
"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,
|
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"
|
CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
|
||||||
: "Index",
|
: "Index",
|
||||||
CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
|
CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
|
||||||
|
@ -563,6 +563,7 @@ int ospf_flood_through_area(struct ospf_area *area, struct ospf_neighbor *inbr,
|
|||||||
struct ospf_interface *oi;
|
struct ospf_interface *oi;
|
||||||
int lsa_ack_flag = 0;
|
int lsa_ack_flag = 0;
|
||||||
|
|
||||||
|
assert(area);
|
||||||
/* All other types are specific to a single area (Area A). The
|
/* All other types are specific to a single area (Area A). The
|
||||||
eligible interfaces are all those interfaces attaching to the
|
eligible interfaces are all those interfaces attaching to the
|
||||||
Area A. If Area A is the backbone, this includes all the virtual
|
Area A. If Area A is the backbone, this includes all the virtual
|
||||||
|
@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
|
|||||||
return ret;
|
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_init(struct ospf *ospf)
|
||||||
{
|
{
|
||||||
int ospf_sock;
|
int ospf_sock;
|
||||||
int ret, hincl = 1;
|
int ret, hincl = 1;
|
||||||
int bufsize = (8 * 1024 * 1024);
|
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)) {
|
if (ospfd_privs.change(ZPRIVS_RAISE)) {
|
||||||
zlog_err("ospf_sock_init: could not raise privs, %s",
|
zlog_err("ospf_sock_init: could not raise privs, %s",
|
||||||
safe_strerror(errno));
|
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) {
|
if (ospf_sock < 0) {
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
|
|
||||||
@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ospf_bind_vrfdevice(ospf, ospf_sock);
|
|
||||||
if (ret < 0) {
|
|
||||||
close(ospf_sock);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef IP_HDRINCL
|
#ifdef IP_HDRINCL
|
||||||
/* we will include IP header with packet */
|
/* we will include IP header with packet */
|
||||||
ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl,
|
ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl,
|
||||||
|
@ -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_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t);
|
||||||
extern int ospf_if_ipmulticast(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_sock_init(struct ospf *ospf);
|
||||||
extern int ospf_bind_vrfdevice(struct ospf *, int);
|
|
||||||
|
|
||||||
#endif /* _ZEBRA_OSPF_NETWORK_H */
|
#endif /* _ZEBRA_OSPF_NETWORK_H */
|
||||||
|
@ -75,6 +75,7 @@ static void ospf_opaque_funclist_init(void);
|
|||||||
static void ospf_opaque_funclist_term(void);
|
static void ospf_opaque_funclist_term(void);
|
||||||
static void free_opaque_info_per_type(void *val);
|
static void free_opaque_info_per_type(void *val);
|
||||||
static void free_opaque_info_per_id(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_install_hook(struct ospf_lsa *lsa);
|
||||||
static int ospf_opaque_lsa_delete_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) {
|
if (functab->opaque_type == opaque_type) {
|
||||||
/* Cleanup internal control information, if it
|
/* Cleanup internal control information, if it
|
||||||
* still remains. */
|
* still remains. */
|
||||||
if (functab->oipt != NULL)
|
if (functab->oipt != NULL) {
|
||||||
free_opaque_info_per_type(
|
free_opaque_info_per_type(
|
||||||
functab->oipt);
|
functab->oipt);
|
||||||
|
free_opaque_info_owner(functab->oipt);
|
||||||
|
}
|
||||||
|
|
||||||
/* Dequeue listnode entry from the list. */
|
/* Dequeue listnode entry from the list. */
|
||||||
listnode_delete(funclist, functab);
|
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);
|
top = ospf_lookup_by_vrf_id(new->vrf_id);
|
||||||
if (new->area != NULL && (top = new->area->ospf) == NULL) {
|
if (new->area != NULL && (top = new->area->ospf) == NULL) {
|
||||||
free_opaque_info_per_type((void *)oipt);
|
free_opaque_info_per_type((void *)oipt);
|
||||||
|
free_opaque_info_owner(oipt);
|
||||||
oipt = NULL;
|
oipt = NULL;
|
||||||
goto out; /* This case may not exist. */
|
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)",
|
"register_opaque_info_per_type: Unexpected LSA-type(%u)",
|
||||||
new->data->type);
|
new->data->type);
|
||||||
free_opaque_info_per_type((void *)oipt);
|
free_opaque_info_per_type((void *)oipt);
|
||||||
|
free_opaque_info_owner(oipt);
|
||||||
oipt = NULL;
|
oipt = NULL;
|
||||||
goto out; /* This case may not exist. */
|
goto out; /* This case may not exist. */
|
||||||
}
|
}
|
||||||
@ -600,6 +605,35 @@ out:
|
|||||||
return oipt;
|
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)
|
static void free_opaque_info_per_type(void *val)
|
||||||
{
|
{
|
||||||
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
|
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
|
||||||
|
@ -3897,6 +3897,10 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi,
|
|||||||
zlog_debug("listcount = %d, [%s]dst %s", listcount(update),
|
zlog_debug("listcount = %d, [%s]dst %s", listcount(update),
|
||||||
IF_NAME(oi), inet_ntoa(addr));
|
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);
|
op = ospf_ls_upd_packet_new(update, oi);
|
||||||
|
|
||||||
/* Prepare OSPF common header. */
|
/* Prepare OSPF common header. */
|
||||||
|
@ -337,6 +337,7 @@ static struct route_map_rule_cmd route_match_tag_cmd = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ospf_metric {
|
struct ospf_metric {
|
||||||
|
enum { metric_increment, metric_decrement, metric_absolute } type;
|
||||||
bool used;
|
bool used;
|
||||||
u_int32_t metric;
|
u_int32_t metric;
|
||||||
};
|
};
|
||||||
@ -356,8 +357,19 @@ static route_map_result_t route_set_metric(void *rule, struct prefix *prefix,
|
|||||||
ei = object;
|
ei = object;
|
||||||
|
|
||||||
/* Set metric out value. */
|
/* 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;
|
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;
|
return RMAP_OKAY;
|
||||||
}
|
}
|
||||||
@ -370,22 +382,27 @@ static void *route_set_metric_compile(const char *arg)
|
|||||||
metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_int32_t));
|
metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_int32_t));
|
||||||
metric->used = false;
|
metric->used = false;
|
||||||
|
|
||||||
/* OSPF doesn't support the +/- in
|
if (all_digit(arg))
|
||||||
set metric <+/-metric> check
|
metric->type = metric_absolute;
|
||||||
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 (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) {
|
||||||
|
zlog_warn("OSPF does not support 'set metric +rtt / -rtt'");
|
||||||
return metric;
|
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->metric = strtoul(arg, NULL, 10);
|
||||||
|
|
||||||
|
if (metric->metric)
|
||||||
metric->used = true;
|
metric->used = true;
|
||||||
|
|
||||||
return metric;
|
return metric;
|
||||||
|
@ -149,14 +149,6 @@ static struct sr_node *sr_node_new(struct in_addr *rid)
|
|||||||
new->ext_link->del = del_sr_link;
|
new->ext_link->del = del_sr_link;
|
||||||
new->ext_prefix->del = del_sr_pref;
|
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);
|
IPV4_ADDR_COPY(&new->adv_router, rid);
|
||||||
new->neighbor = NULL;
|
new->neighbor = NULL;
|
||||||
new->instance = 0;
|
new->instance = 0;
|
||||||
@ -440,7 +432,7 @@ static struct ospf_path *get_nexthop_by_addr(struct ospf *top,
|
|||||||
struct route_node *rn;
|
struct route_node *rn;
|
||||||
|
|
||||||
/* Sanity Check */
|
/* Sanity Check */
|
||||||
if ((top == NULL) && (top->new_table))
|
if (top == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (IS_DEBUG_OSPF_SR)
|
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[0].ifindex = nh->oi->ifp->ifindex;
|
||||||
srl->nhlfe[1].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 */
|
/* Set Input & Output Label */
|
||||||
if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
|
if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
|
||||||
srl->nhlfe[0].label_in = srl->sid[0];
|
srl->nhlfe[0].label_in = srl->sid[0];
|
||||||
|
@ -49,11 +49,10 @@
|
|||||||
/* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */
|
/* 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) */
|
/* 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 3
|
||||||
#define SID_LABEL_SIZE (SID_BASE_SIZE + SID_LABEL)
|
#define SID_LABEL_SIZE(U) (U - 1)
|
||||||
#define SID_INDEX 4
|
#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 */
|
/* SID/Label Sub TLV - section 2.1 */
|
||||||
#define SUBTLV_SID_LABEL 1
|
#define SUBTLV_SID_LABEL 1
|
||||||
|
@ -3693,7 +3693,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
|
|||||||
{
|
{
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
|
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;
|
json_object *json_interface_sub = NULL, *json_interface = NULL;
|
||||||
|
|
||||||
if (use_json) {
|
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();
|
json_vrf = json_object_new_object();
|
||||||
else
|
else
|
||||||
json_vrf = json;
|
json_vrf = json;
|
||||||
json_intf_array = json_object_new_array();
|
json_interface = json_object_new_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ospf->instance) {
|
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);
|
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
|
||||||
|
|
||||||
if (intf_name == NULL) {
|
if (intf_name == NULL) {
|
||||||
if (use_json)
|
|
||||||
json_object_object_add(json_vrf, "interfaces",
|
|
||||||
json_intf_array);
|
|
||||||
/* Show All Interfaces.*/
|
/* Show All Interfaces.*/
|
||||||
FOR_ALL_INTERFACES (vrf, ifp) {
|
FOR_ALL_INTERFACES (vrf, ifp) {
|
||||||
if (ospf_oi_count(ifp)) {
|
if (ospf_oi_count(ifp)) {
|
||||||
if (use_json) {
|
if (use_json) {
|
||||||
json_interface =
|
|
||||||
json_object_new_object();
|
|
||||||
json_interface_sub =
|
json_interface_sub =
|
||||||
json_object_new_object();
|
json_object_new_object();
|
||||||
}
|
}
|
||||||
@ -3732,14 +3727,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
|
|||||||
use_json);
|
use_json);
|
||||||
|
|
||||||
if (use_json) {
|
if (use_json) {
|
||||||
json_object_array_add(json_intf_array,
|
|
||||||
json_interface);
|
|
||||||
json_object_object_add(
|
json_object_object_add(
|
||||||
json_interface, ifp->name,
|
json_interface, ifp->name,
|
||||||
json_interface_sub);
|
json_interface_sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (use_json)
|
||||||
|
json_object_object_add(json_vrf, "interfaces",
|
||||||
|
json_interface);
|
||||||
} else {
|
} else {
|
||||||
/* Interface name is specified. */
|
/* Interface name is specified. */
|
||||||
ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
|
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) {
|
if (use_json) {
|
||||||
json_interface_sub = json_object_new_object();
|
json_interface_sub = json_object_new_object();
|
||||||
json_interface = 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(
|
show_ip_ospf_interface_sub(
|
||||||
vty, ospf, ifp, json_interface_sub, use_json);
|
vty, ospf, ifp, json_interface_sub, use_json);
|
||||||
|
|
||||||
if (use_json) {
|
if (use_json) {
|
||||||
json_object_array_add(json_intf_array,
|
|
||||||
json_interface);
|
|
||||||
json_object_object_add(json_interface,
|
json_object_object_add(json_interface,
|
||||||
ifp->name,
|
ifp->name,
|
||||||
json_interface_sub);
|
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 ospf_interface *oi;
|
||||||
struct listnode *node;
|
struct listnode *node;
|
||||||
json_object *json_vrf = NULL, *json_nbr_array = NULL;
|
json_object *json_vrf = NULL;
|
||||||
json_object *json_nbr_sub = NULL;
|
json_object *json_nbr_sub = NULL;
|
||||||
|
|
||||||
if (use_json) {
|
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();
|
json_vrf = json_object_new_object();
|
||||||
else
|
else
|
||||||
json_vrf = json;
|
json_vrf = json;
|
||||||
json_nbr_array = json_object_new_array();
|
json_nbr_sub = json_object_new_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ospf->instance) {
|
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);
|
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
|
||||||
if (!use_json)
|
if (!use_json)
|
||||||
show_ip_ospf_neighbour_header(vty);
|
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)) {
|
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
|
||||||
if (ospf_interface_neighbor_count(oi) == 0)
|
if (ospf_interface_neighbor_count(oi) == 0)
|
||||||
continue;
|
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);
|
show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_json) {
|
if (use_json) {
|
||||||
|
json_object_object_add(json_vrf, "neighbors",
|
||||||
|
json_nbr_sub);
|
||||||
if (use_vrf) {
|
if (use_vrf) {
|
||||||
if (ospf->vrf_id == VRF_DEFAULT)
|
if (ospf->vrf_id == VRF_DEFAULT)
|
||||||
json_object_object_add(json, "default",
|
json_object_object_add(json, "default",
|
||||||
@ -8558,6 +8547,10 @@ DEFUN (ospf_distance_ospf,
|
|||||||
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
|
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
|
ospf->distance_intra = 0;
|
||||||
|
ospf->distance_inter = 0;
|
||||||
|
ospf->distance_external = 0;
|
||||||
|
|
||||||
if (argv_find(argv, argc, "intra-area", &idx))
|
if (argv_find(argv, argc, "intra-area", &idx))
|
||||||
ospf->distance_intra = atoi(argv[idx + 1]->arg);
|
ospf->distance_intra = atoi(argv[idx + 1]->arg);
|
||||||
idx = 0;
|
idx = 0;
|
||||||
|
@ -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_refresh_interval, &new->t_lsa_refresher);
|
||||||
new->lsa_refresher_started = monotime(NULL);
|
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) {
|
if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) {
|
||||||
zlog_err(
|
zlog_err(
|
||||||
"ospf_new: fatal error: stream_new(%u) failed allocating ibuf",
|
"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);
|
exit(1);
|
||||||
}
|
}
|
||||||
new->t_read = NULL;
|
new->t_read = NULL;
|
||||||
thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
|
|
||||||
new->oi_write_q = list_new();
|
new->oi_write_q = list_new();
|
||||||
new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT;
|
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);
|
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;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2049,7 +2052,8 @@ static int ospf_vrf_delete(struct vrf *vrf)
|
|||||||
static int ospf_vrf_enable(struct vrf *vrf)
|
static int ospf_vrf_enable(struct vrf *vrf)
|
||||||
{
|
{
|
||||||
struct ospf *ospf = NULL;
|
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)
|
if (IS_DEBUG_OSPF_EVENT)
|
||||||
zlog_debug("%s: VRF %s id %u enabled",
|
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",
|
zlog_err("ospf_sock_init: could not raise privs, %s",
|
||||||
safe_strerror(errno));
|
safe_strerror(errno));
|
||||||
}
|
}
|
||||||
if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0)
|
ret = ospf_sock_init(ospf);
|
||||||
return 0;
|
|
||||||
if (ospfd_privs.change(ZPRIVS_LOWER)) {
|
if (ospfd_privs.change(ZPRIVS_LOWER)) {
|
||||||
zlog_err("ospf_sock_init: could not lower privs, %s",
|
zlog_err("ospf_sock_init: could not lower privs, %s",
|
||||||
safe_strerror(errno));
|
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->oi_running = 1;
|
||||||
ospf_zebra_vrf_register(ospf);
|
ospf_zebra_vrf_register(ospf);
|
||||||
ospf_router_id_update(ospf);
|
ospf_router_id_update(ospf);
|
||||||
@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf)
|
|||||||
if (IS_DEBUG_OSPF_EVENT)
|
if (IS_DEBUG_OSPF_EVENT)
|
||||||
zlog_debug("%s: ospf old_vrf_id %d unlinked",
|
zlog_debug("%s: ospf old_vrf_id %d unlinked",
|
||||||
__PRETTY_FUNCTION__, old_vrf_id);
|
__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. */
|
/* Note: This is a callback, the VRF will be deleted by the caller. */
|
||||||
|
1
pimd/.gitignore
vendored
1
pimd/.gitignore
vendored
@ -2,6 +2,7 @@
|
|||||||
Makefile.in
|
Makefile.in
|
||||||
libpim.a
|
libpim.a
|
||||||
pimd
|
pimd
|
||||||
|
mtracebis
|
||||||
test_igmpv3_join
|
test_igmpv3_join
|
||||||
tags
|
tags
|
||||||
TAGS
|
TAGS
|
||||||
|
@ -173,4 +173,10 @@ C19 Provision to prevent group mode clash
|
|||||||
flow. There could be some provision to prevent such a behavior,
|
flow. There could be some provision to prevent such a behavior,
|
||||||
but currently there is none.
|
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-
|
-x-
|
||||||
|
@ -59,6 +59,7 @@ debug commands:
|
|||||||
clear ip pim interfaces Reset PIM interfaces
|
clear ip pim interfaces Reset PIM interfaces
|
||||||
clear ip pim oil Rescan PIM OIL (output interface list)
|
clear ip pim oil Rescan PIM OIL (output interface list)
|
||||||
debug igmp IGMP protocol activity
|
debug igmp IGMP protocol activity
|
||||||
|
debug mtrace Mtrace protocol activity
|
||||||
debug mroute PIM interaction with kernel MFC cache
|
debug mroute PIM interaction with kernel MFC cache
|
||||||
debug pim PIM protocol activity
|
debug pim PIM protocol activity
|
||||||
debug pim zebra ZEBRA protocol activity
|
debug pim zebra ZEBRA protocol activity
|
||||||
@ -76,4 +77,6 @@ statistics commands:
|
|||||||
pimd:
|
pimd:
|
||||||
show memory pim PIM memory statistics
|
show memory pim PIM memory statistics
|
||||||
|
|
||||||
|
vtysh:
|
||||||
|
mtrace Multicast traceroute
|
||||||
-x-
|
-x-
|
||||||
|
372
pimd/TODO
372
pimd/TODO
@ -1,375 +1,21 @@
|
|||||||
T1 DONE Implement debug command
|
T1 Consider reliable pim solution (refresh reduction)
|
||||||
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)
|
|
||||||
A Reliable Transport Mechanism for PIM
|
A Reliable Transport Mechanism for PIM
|
||||||
http://tools.ietf.org/wg/pim/draft-ietf-pim-port/
|
http://tools.ietf.org/wg/pim/draft-ietf-pim-port/
|
||||||
PORT=PIM-Over-Reliable-Transport
|
PORT=PIM-Over-Reliable-Transport
|
||||||
|
|
||||||
T19 DONE Fix self as neighbor
|
T2 If an interface changes one of its secondary IP addresses, a Hello
|
||||||
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
|
|
||||||
message with an updated Address_List option and a non-zero
|
message with an updated Address_List option and a non-zero
|
||||||
HoldTime should be sent immediately.
|
HoldTime should be sent immediately.
|
||||||
See also detect_secondary_address_change
|
See also detect_secondary_address_change
|
||||||
See also CAVEAT C15.
|
See also CAVEAT C15.
|
||||||
See also RFC 4601: 4.3.1. Sending Hello Messages
|
See also RFC 4601: 4.3.1. Sending Hello Messages
|
||||||
|
|
||||||
T32 FIXED Detection of interface primary address changes may fail when
|
T3 Lightweight MLDv2
|
||||||
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
|
|
||||||
http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05
|
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/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
|
||||||
http://www.ietf.org/html.charters/mboned-charter.html
|
http://www.ietf.org/html.charters/mboned-charter.html
|
||||||
|
|
||||||
T41 DONE ssmping support
|
T4 Static igmp join fails when loading config at boot time
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
! Wrong behavior seen 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
|
eth0 200.202.112.3 2 2 0 0 0 0
|
||||||
lo 127.0.0.1 1 1 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/
|
https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/
|
||||||
|
|
||||||
"In a transit LAN (no directly connected source or receiver), many
|
"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
|
a procedure to reduce the amount of neighbors established over a
|
||||||
transit LAN."
|
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/
|
https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/
|
||||||
|
|
||||||
"This document proposes an IP multicast fast convergence method
|
"This document proposes an IP multicast fast convergence method
|
||||||
based on differentiating primary and backup PIM join."
|
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
|
"This document describes a modification of the Join message that
|
||||||
allows a node to associate attributes with a particular tree."
|
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
|
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
|
"This draft introduces a new type of PIM Join Attribute used to
|
||||||
encode the identity of the topology PIM uses for RPF."
|
encode the identity of the topology PIM uses for RPF."
|
||||||
|
373
pimd/mtracebis.c
Normal file
373
pimd/mtracebis.c
Normal 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
700
pimd/mtracebis_netlink.c
Normal 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
130
pimd/mtracebis_netlink.h
Normal 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
98
pimd/mtracebis_routeget.c
Normal 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
31
pimd/mtracebis_routeget.h
Normal 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__ */
|
@ -7311,6 +7311,27 @@ DEFUN (no_debug_msdp_packets,
|
|||||||
ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
|
ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
|
||||||
UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
|
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,
|
DEFUN_NOSH (show_debugging_pim,
|
||||||
show_debugging_pim_cmd,
|
show_debugging_pim_cmd,
|
||||||
"show debugging [pim]",
|
"show debugging [pim]",
|
||||||
@ -8721,6 +8742,8 @@ void pim_cmd_init(void)
|
|||||||
install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
|
install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
|
||||||
install_element(ENABLE_NODE, &no_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, &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, &debug_igmp_cmd);
|
||||||
install_element(CONFIG_NODE, &no_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, &debug_msdp_packets_cmd);
|
||||||
install_element(CONFIG_NODE, &no_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, &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(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
|
||||||
install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);
|
install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n"
|
#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n"
|
||||||
#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n"
|
#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n"
|
||||||
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
|
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
|
||||||
|
#define DEBUG_MTRACE_STR "Mtrace protocol activity\n"
|
||||||
|
|
||||||
void pim_cmd_init(void);
|
void pim_cmd_init(void);
|
||||||
|
|
||||||
|
@ -85,9 +85,11 @@ static void *if_list_clean(struct pim_interface *pim_ifp)
|
|||||||
if (pim_ifp->sec_addr_list)
|
if (pim_ifp->sec_addr_list)
|
||||||
list_delete_and_null(&pim_ifp->sec_addr_list);
|
list_delete_and_null(&pim_ifp->sec_addr_list);
|
||||||
|
|
||||||
while ((ch = RB_ROOT(pim_ifchannel_rb,
|
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
|
||||||
&pim_ifp->ifchannel_rb)) != NULL)
|
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
|
||||||
|
|
||||||
pim_ifchannel_delete(ch);
|
pim_ifchannel_delete(ch);
|
||||||
|
}
|
||||||
|
|
||||||
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
|
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
|
||||||
|
|
||||||
@ -250,9 +252,11 @@ void pim_if_delete(struct interface *ifp)
|
|||||||
if (pim_ifp->boundary_oil_plist)
|
if (pim_ifp->boundary_oil_plist)
|
||||||
XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
|
XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
|
||||||
|
|
||||||
while ((ch = RB_ROOT(pim_ifchannel_rb,
|
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
|
||||||
&pim_ifp->ifchannel_rb)) != NULL)
|
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
|
||||||
|
|
||||||
pim_ifchannel_delete(ch);
|
pim_ifchannel_delete(ch);
|
||||||
|
}
|
||||||
|
|
||||||
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
|
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
|
||||||
|
|
||||||
|
@ -211,8 +211,9 @@ void pim_ifchannel_delete_all(struct interface *ifp)
|
|||||||
if (!pim_ifp)
|
if (!pim_ifp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while ((ch = RB_ROOT(pim_ifchannel_rb,
|
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
|
||||||
&pim_ifp->ifchannel_rb)) != NULL) {
|
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
|
||||||
|
|
||||||
pim_ifchannel_delete(ch);
|
pim_ifchannel_delete(ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "pim_igmp.h"
|
#include "pim_igmp.h"
|
||||||
#include "pim_igmpv2.h"
|
#include "pim_igmpv2.h"
|
||||||
#include "pim_igmpv3.h"
|
#include "pim_igmpv3.h"
|
||||||
|
#include "pim_igmp_mtrace.h"
|
||||||
#include "pim_iface.h"
|
#include "pim_iface.h"
|
||||||
#include "pim_sock.h"
|
#include "pim_sock.h"
|
||||||
#include "pim_mroute.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:
|
case PIM_IGMP_V2_LEAVE_GROUP:
|
||||||
return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
|
return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
|
||||||
igmp_msg, igmp_msg_len);
|
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);
|
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
|
#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
|
||||||
#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
|
#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
|
||||||
#define PIM_IGMP_V2_LEAVE_GROUP (0x17)
|
#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 PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
|
||||||
|
|
||||||
#define IGMP_V3_REPORT_HEADER_SIZE (8)
|
#define IGMP_V3_REPORT_HEADER_SIZE (8)
|
||||||
|
727
pimd/pim_igmp_mtrace.c
Normal file
727
pimd/pim_igmp_mtrace.c
Normal 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
103
pimd/pim_igmp_mtrace.h
Normal 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 */
|
@ -135,7 +135,7 @@ void pim_channel_oil_free(struct channel_oil *c_oil)
|
|||||||
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
|
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
|
struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
|
||||||
struct prefix_sg *sg)
|
struct prefix_sg *sg)
|
||||||
{
|
{
|
||||||
struct channel_oil *c_oil = NULL;
|
struct channel_oil *c_oil = NULL;
|
||||||
|
@ -86,6 +86,8 @@ void pim_oil_init(struct pim_instance *pim);
|
|||||||
void pim_oil_terminate(struct pim_instance *pim);
|
void pim_oil_terminate(struct pim_instance *pim);
|
||||||
|
|
||||||
void pim_channel_oil_free(struct channel_oil *c_oil);
|
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 channel_oil *pim_channel_oil_add(struct pim_instance *pim,
|
||||||
struct prefix_sg *sg,
|
struct prefix_sg *sg,
|
||||||
int input_vif_index);
|
int input_vif_index);
|
||||||
|
@ -78,6 +78,11 @@ int pim_debug_config_write(struct vty *vty)
|
|||||||
++writes;
|
++writes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PIM_DEBUG_MTRACE) {
|
||||||
|
vty_out(vty, "debug mtrace\n");
|
||||||
|
++writes;
|
||||||
|
}
|
||||||
|
|
||||||
if (PIM_DEBUG_MROUTE_DETAIL) {
|
if (PIM_DEBUG_MROUTE_DETAIL) {
|
||||||
vty_out(vty, "debug mroute detail\n");
|
vty_out(vty, "debug mroute detail\n");
|
||||||
++writes;
|
++writes;
|
||||||
|
@ -112,6 +112,7 @@
|
|||||||
#define PIM_MASK_PIM_NHT (1 << 22)
|
#define PIM_MASK_PIM_NHT (1 << 22)
|
||||||
#define PIM_MASK_PIM_NHT_DETAIL (1 << 23)
|
#define PIM_MASK_PIM_NHT_DETAIL (1 << 23)
|
||||||
#define PIM_MASK_PIM_NHT_RP (1 << 24)
|
#define PIM_MASK_PIM_NHT_RP (1 << 24)
|
||||||
|
#define PIM_MASK_MTRACE (1 << 25)
|
||||||
/* Remember 32 bits!!! */
|
/* Remember 32 bits!!! */
|
||||||
|
|
||||||
/* PIM error codes */
|
/* 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 (qpim_debugs & PIM_MASK_PIM_NHT)
|
||||||
#define PIM_DEBUG_PIM_NHT_DETAIL (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL)
|
#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_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_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))
|
#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_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 (qpim_debugs |= PIM_MASK_PIM_NHT)
|
||||||
#define PIM_DO_DEBUG_PIM_NHT_RP (qpim_debugs |= PIM_MASK_PIM_NHT_RP)
|
#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_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
|
||||||
#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
|
#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_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 (qpim_debugs &= ~PIM_MASK_PIM_NHT)
|
||||||
#define PIM_DONT_DEBUG_PIM_NHT_RP (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP)
|
#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_init(void);
|
||||||
void pim_terminate(void);
|
void pim_terminate(void);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
if PIMD
|
if PIMD
|
||||||
noinst_LIBRARIES += pimd/libpim.a
|
noinst_LIBRARIES += pimd/libpim.a
|
||||||
sbin_PROGRAMS += pimd/pimd
|
sbin_PROGRAMS += pimd/pimd
|
||||||
|
bin_PROGRAMS += pimd/mtracebis
|
||||||
noinst_PROGRAMS += pimd/test_igmpv3_join
|
noinst_PROGRAMS += pimd/test_igmpv3_join
|
||||||
dist_examples_DATA += pimd/pimd.conf.sample
|
dist_examples_DATA += pimd/pimd.conf.sample
|
||||||
endif
|
endif
|
||||||
@ -18,6 +19,7 @@ pimd_libpim_a_SOURCES = \
|
|||||||
pimd/pim_iface.c \
|
pimd/pim_iface.c \
|
||||||
pimd/pim_ifchannel.c \
|
pimd/pim_ifchannel.c \
|
||||||
pimd/pim_igmp.c \
|
pimd/pim_igmp.c \
|
||||||
|
pimd/pim_igmp_mtrace.c \
|
||||||
pimd/pim_igmpv2.c \
|
pimd/pim_igmpv2.c \
|
||||||
pimd/pim_igmpv3.c \
|
pimd/pim_igmpv3.c \
|
||||||
pimd/pim_instance.c \
|
pimd/pim_instance.c \
|
||||||
@ -66,6 +68,7 @@ noinst_HEADERS += \
|
|||||||
pimd/pim_ifchannel.h \
|
pimd/pim_ifchannel.h \
|
||||||
pimd/pim_igmp.h \
|
pimd/pim_igmp.h \
|
||||||
pimd/pim_igmp_join.h \
|
pimd/pim_igmp_join.h \
|
||||||
|
pimd/pim_igmp_mtrace.h \
|
||||||
pimd/pim_igmpv2.h \
|
pimd/pim_igmpv2.h \
|
||||||
pimd/pim_igmpv3.h \
|
pimd/pim_igmpv3.h \
|
||||||
pimd/pim_instance.h \
|
pimd/pim_instance.h \
|
||||||
@ -101,6 +104,8 @@ noinst_HEADERS += \
|
|||||||
pimd/pim_zebra.h \
|
pimd/pim_zebra.h \
|
||||||
pimd/pim_zlookup.h \
|
pimd/pim_zlookup.h \
|
||||||
pimd/pimd.h \
|
pimd/pimd.h \
|
||||||
|
pimd/mtracebis_netlink.h \
|
||||||
|
pimd/mtracebis_routeget.h \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@
|
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_LDADD = lib/libfrr.la
|
||||||
pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
|
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
|
||||||
|
@ -80,14 +80,17 @@ DEFPY (install_routes,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEFPY(vrf_label, vrf_label_cmd,
|
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"
|
"Sharp Routing Protocol\n"
|
||||||
"Give a vrf a label\n"
|
"Give a vrf a label\n"
|
||||||
|
"Pop and forward for IPv4\n"
|
||||||
|
"Pop and forward for IPv6\n"
|
||||||
VRF_CMD_HELP_STR
|
VRF_CMD_HELP_STR
|
||||||
"The label to use, 0 specifies remove the label installed from previous\n"
|
"The label to use, 0 specifies remove the label installed from previous\n"
|
||||||
"Specified range to use\n")
|
"Specified range to use\n")
|
||||||
{
|
{
|
||||||
struct vrf *vrf;
|
struct vrf *vrf;
|
||||||
|
afi_t afi = (ipv4) ? AFI_IP : AFI_IP6;
|
||||||
|
|
||||||
if (strcmp(name, "default") == 0)
|
if (strcmp(name, "default") == 0)
|
||||||
vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
||||||
@ -102,7 +105,7 @@ DEFPY(vrf_label, vrf_label_cmd,
|
|||||||
if (label == 0)
|
if (label == 0)
|
||||||
label = MPLS_LABEL_NONE;
|
label = MPLS_LABEL_NONE;
|
||||||
|
|
||||||
vrf_label_add(vrf->vrf_id, label);
|
vrf_label_add(vrf->vrf_id, afi, label);
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,13 +131,14 @@ static int interface_state_down(int command, struct zclient *zclient,
|
|||||||
extern uint32_t total_routes;
|
extern uint32_t total_routes;
|
||||||
extern uint32_t installed_routes;
|
extern uint32_t installed_routes;
|
||||||
|
|
||||||
static int notify_owner(int command, struct zclient *zclient,
|
static int route_notify_owner(int command, struct zclient *zclient,
|
||||||
zebra_size_t length, vrf_id_t vrf_id)
|
zebra_size_t length, vrf_id_t vrf_id)
|
||||||
{
|
{
|
||||||
struct prefix p;
|
struct prefix p;
|
||||||
enum zapi_route_notify_owner note;
|
enum zapi_route_notify_owner note;
|
||||||
|
uint32_t table;
|
||||||
|
|
||||||
if (!zapi_route_notify_decode(zclient->ibuf, &p, ¬e))
|
if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
installed_routes++;
|
installed_routes++;
|
||||||
@ -152,9 +153,9 @@ static void zebra_connected(struct zclient *zclient)
|
|||||||
zclient_send_reg_requests(zclient, VRF_DEFAULT);
|
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)
|
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;
|
api.safi = SAFI_UNICAST;
|
||||||
memcpy(&api.prefix, p, sizeof(*p));
|
memcpy(&api.prefix, p, sizeof(*p));
|
||||||
|
|
||||||
|
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
|
||||||
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
||||||
|
|
||||||
api_nh = &api.nexthops[0];
|
api_nh = &api.nexthops[0];
|
||||||
@ -210,5 +212,5 @@ void sharp_zebra_init(void)
|
|||||||
zclient->interface_down = interface_state_down;
|
zclient->interface_down = interface_state_down;
|
||||||
zclient->interface_address_add = interface_address_add;
|
zclient->interface_address_add = interface_address_add;
|
||||||
zclient->interface_address_delete = interface_address_delete;
|
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
Loading…
Reference in New Issue
Block a user