]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #1764 from Orange-OpenSource/SR-dev
authorRuss White <russ@riw.us>
Tue, 27 Feb 2018 13:56:33 +0000 (08:56 -0500)
committerGitHub <noreply@github.com>
Tue, 27 Feb 2018 13:56:33 +0000 (08:56 -0500)
OSPFD: Fix Segment Routing Lan Adjacency TLVs

121 files changed:
bgpd/bgp_dump.c
bgpd/bgp_evpn.c
bgpd/bgp_evpn.h
bgpd/bgp_evpn_private.h
bgpd/bgp_evpn_vty.c
bgpd/bgp_fsm.c
bgpd/bgp_keepalives.c
bgpd/bgp_main.c
bgpd/bgp_mpath.c
bgpd/bgp_network.c
bgpd/bgp_network.h
bgpd/bgp_vty.c
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/bgpd.h
configure.ac
debianpkg/backports/ubuntu14.04/debian/frr.install
debianpkg/frr.install
doc/Building_FRR_on_CentOS7.md
doc/Makefile.am
doc/mtracebis.8.in [new file with mode: 0644]
doc/zebra.8.in
eigrpd/eigrp_zebra.c
git-reindent-branch.py [deleted file]
include/linux/net_namespace.h [new file with mode: 0644]
include/subdir.am
indent.py [deleted file]
isisd/isis_circuit.c
ldpd/interface.c
ldpd/l2vpn.c
ldpd/lde.c
ldpd/lde_lib.c
ldpd/ldp_vty_conf.c
ldpd/ldpd.c
ldpd/ldpe.c
ldpd/neighbor.c
lib/command.c
lib/command.h
lib/if.c
lib/logicalrouter.c [new file with mode: 0644]
lib/logicalrouter.h [new file with mode: 0644]
lib/netns_linux.c [new file with mode: 0644]
lib/netns_other.c [new file with mode: 0644]
lib/ns.c [deleted file]
lib/ns.h
lib/prefix.h
lib/subdir.am
lib/vrf.c
lib/vrf.h
lib/zclient.c
lib/zclient.h
ospfd/ospf_network.c
ospfd/ospf_network.h
ospfd/ospf_opaque.c
ospfd/ospf_packet.c
ospfd/ospf_routemap.c
ospfd/ospf_vty.c
ospfd/ospfd.c
pimd/.gitignore
pimd/CAVEATS
pimd/COMMANDS
pimd/mtracebis.c [new file with mode: 0644]
pimd/mtracebis_netlink.c [new file with mode: 0644]
pimd/mtracebis_netlink.h [new file with mode: 0644]
pimd/mtracebis_routeget.c [new file with mode: 0644]
pimd/mtracebis_routeget.h [new file with mode: 0644]
pimd/pim_cmd.c
pimd/pim_cmd.h
pimd/pim_iface.c
pimd/pim_ifchannel.c
pimd/pim_igmp.c
pimd/pim_igmp.h
pimd/pim_igmp_mtrace.c [new file with mode: 0644]
pimd/pim_igmp_mtrace.h [new file with mode: 0644]
pimd/pim_oil.c
pimd/pim_oil.h
pimd/pim_vty.c
pimd/pimd.h
pimd/subdir.am
render_md.py [deleted file]
sharpd/sharp_zebra.c
tools/checkpatch.pl
tools/checkpatch.sh
tools/git-reindent-branch.py [new file with mode: 0644]
tools/indent.py [new file with mode: 0644]
tools/render_md.py [new file with mode: 0644]
vtysh/Makefile.am
vtysh/extract.pl.in
vtysh/vtysh.c
vtysh/vtysh_config.c
vtysh/vtysh_main.c
zebra/if_ioctl.c
zebra/if_netlink.c
zebra/interface.c
zebra/ioctl.c
zebra/ioctl.h
zebra/ioctl_solaris.c
zebra/kernel_netlink.c
zebra/kernel_socket.c
zebra/main.c
zebra/rt.h
zebra/rt_netlink.c
zebra/rt_socket.c
zebra/rtadv.c
zebra/subdir.am
zebra/zebra_netns_id.c [new file with mode: 0644]
zebra/zebra_netns_id.h [new file with mode: 0644]
zebra/zebra_netns_notify.c [new file with mode: 0644]
zebra/zebra_netns_notify.h [new file with mode: 0644]
zebra/zebra_ns.c
zebra/zebra_ns.h
zebra/zebra_pw.c
zebra/zebra_rib.c
zebra/zebra_vrf.c
zebra/zebra_vrf.h
zebra/zebra_vty.c
zebra/zebra_vxlan.c
zebra/zebra_vxlan.h
zebra/zebra_vxlan_private.h
zebra/zserv.c
zebra/zserv.h

index 08313690624631ce9b47923e79663829a3414fe6..4e998b1fd9cd5cdd498aa37533de0ca696a56036 100644 (file)
@@ -465,7 +465,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer,
        }
 
        if (peer->su.sa.sa_family == AF_INET) {
-               stream_putw(obuf, peer->ifindex);
+               stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
                stream_putw(obuf, AFI_IP);
 
                stream_put(obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
@@ -477,7 +477,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer,
                        stream_put(obuf, empty, IPV4_MAX_BYTELEN);
        } else if (peer->su.sa.sa_family == AF_INET6) {
                /* Interface Index and Address family. */
-               stream_putw(obuf, peer->ifindex);
+               stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
                stream_putw(obuf, AFI_IP6);
 
                /* Source IP Address and Destination IP Address. */
index ec8e2907a69c5b641005259c6df6f30ccbd34627..e21a08c2687e9df69c80acf038bdd25f6da203c7 100644 (file)
@@ -666,10 +666,11 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
        for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
                attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom);
 
-       /* Add the export RTs for L3VNI - currently only supported for IPV4 host
-        * routes
+       /*
+        * only attach l3-vni export rts for ipv4 address family and if we are
+        * advertising both the labels in type-2 routes
         */
-       if (afi == AFI_IP) {
+       if (afi == AFI_IP && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
                vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
                if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
                        for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
@@ -690,7 +691,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
                        ecommunity_merge(attr->ecommunity, &ecom_sticky);
        }
 
-       if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) {
+       /*
+        * only attach l3-vni rmac for ipv4 address family and if we are
+        * advertising both the labels in type-2 routes
+        */
+       if (afi == AFI_IP && !is_zero_mac(&attr->rmac) &&
+           CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
                memset(&ecom_rmac, 0, sizeof(ecom_rmac));
                encode_rmac_extcomm(&eval_rmac, &attr->rmac);
                ecom_rmac.size = 1;
@@ -1189,8 +1195,13 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
 
                /* The VNI goes into the 'label' field of the route */
                vni2label(vpn->vni, &label[0]);
-               /* Type-2 routes may carry a second VNI - the L3-VNI */
-               if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+
+               /* Type-2 routes may carry a second VNI - the L3-VNI.
+                * Only attach second label if we are advertising two labels for
+                * type-2 routes.
+                */
+               if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
+                   CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
                        vni_t l3vni;
 
                        l3vni = bgpevpn_get_l3vni(vpn);
@@ -1209,6 +1220,24 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
                    && !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
                        route_change = 0;
                else {
+                       /*
+                        * The attributes have changed, type-2 routes needs to
+                        * be advertised with right labels.
+                        */
+                       vni2label(vpn->vni, &label[0]);
+                       if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
+                           CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
+                               vni_t l3vni;
+
+                               l3vni = bgpevpn_get_l3vni(vpn);
+                               if (l3vni) {
+                                       vni2label(l3vni, &label[1]);
+                                       num_labels++;
+                               }
+                       }
+                       memcpy(&tmp_ri->extra->label, label, sizeof(label));
+                       tmp_ri->extra->num_labels = num_labels;
+
                        /* The attribute has changed. */
                        /* Add (or update) attribute to hash. */
                        attr_new = bgp_attr_intern(attr);
@@ -4227,7 +4256,8 @@ static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
 int bgp_evpn_local_l3vni_add(vni_t l3vni,
                             vrf_id_t vrf_id,
                             struct ethaddr *rmac,
-                            struct in_addr originator_ip)
+                            struct in_addr originator_ip,
+                            int filter)
 {
        struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
        struct bgp *bgp_def = NULL; /* default bgp instance */
@@ -4279,6 +4309,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
        /* set the originator ip */
        bgp_vrf->originator_ip = originator_ip;
 
+       /* set the right filter - are we using l3vni only for prefix routes? */
+       if (filter)
+               SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
+
        /* auto derive RD/RT */
        if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
                evpn_auto_rt_import_add_for_vrf(bgp_vrf);
@@ -4292,9 +4326,12 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
                        link_l2vni_hash_to_l3vni,
                     bgp_vrf);
 
-       /* updates all corresponding local mac-ip routes */
-       for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
-               update_routes_for_vni(bgp_def, vpn);
+       /* Only update all corresponding type-2 routes if we are advertising two
+        * labels along with type-2 routes
+        */
+       if (!filter)
+               for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+                       update_routes_for_vni(bgp_def, vpn);
 
        /* advertise type-5 routes if needed */
        update_advertise_vrf_routes(bgp_vrf);
@@ -4353,9 +4390,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni,
        }
 
        /* update all corresponding local mac-ip routes */
-       for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
-               update_routes_for_vni(bgp_def, vpn);
-
+       if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) {
+               for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
+                       UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+                       update_routes_for_vni(bgp_def, vpn);
+               }
+       }
 
        /* Delete the instance if it was autocreated */
        if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
index a8dcbc112b1cc18b4313bec527e4f4d0ecd95403..d8d92618f6da20baaa1bcebdce709dc64cbcd671 100644 (file)
@@ -91,7 +91,8 @@ extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
                                    u_char flags);
 extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
                                    struct ethaddr *rmac,
-                                   struct in_addr originator_ip);
+                                   struct in_addr originator_ip,
+                                   int filter);
 extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id);
 extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
 extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
index cc0ec82344b3f2c03cad7cc7a4fd1ca1806d82c5..1dbc1f25f00d8709f3d7dfead71c75ad450b247c 100644 (file)
@@ -61,6 +61,8 @@ struct bgpevpn {
 #define VNI_FLAG_RD_CFGD           0x4  /* RD is user configured. */
 #define VNI_FLAG_IMPRT_CFGD        0x8  /* Import RT is user configured */
 #define VNI_FLAG_EXPRT_CFGD        0x10 /* Export RT is user configured */
+#define VNI_FLAG_USE_TWO_LABELS    0x20 /* Attach both L2-VNI and L3-VNI if
+                                          needed for this VPN */
 
        /* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
        u_int8_t advertise_gw_macip;
@@ -179,9 +181,13 @@ static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
        struct bgp *bgp_vrf = NULL;
 
        bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
-       if (!bgp_vrf || !bgp_vrf->l2vnis)
+       if (!bgp_vrf)
                return;
-       listnode_delete(bgp_vrf->l2vnis, vpn);
+
+       UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+
+       if (bgp_vrf->l2vnis)
+               listnode_delete(bgp_vrf->l2vnis, vpn);
 }
 
 static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
@@ -189,9 +195,16 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
        struct bgp *bgp_vrf = NULL;
 
        bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
-       if (!bgp_vrf || !bgp_vrf->l2vnis)
+       if (!bgp_vrf)
                return;
-       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);
+
+       if (bgp_vrf->l2vnis)
+               listnode_add_sort(bgp_vrf->l2vnis, vpn);
 }
 
 static inline int is_vni_configured(struct bgpevpn *vpn)
index e1e11e44ace97ff70f8a1136d31eb22971719ef0..58c72bf36fddc792dd92e4c6afdad703bca6dc40 100644 (file)
@@ -83,7 +83,7 @@ static void display_vrf_import_rt(struct vty *vty,
        case ECOMMUNITY_ENCODE_AS:
                eas.as = (*pnt++ << 8);
                eas.as |= (*pnt++);
-               pnt = ptr_get_be32(pnt, &eas.val);
+               ptr_get_be32(pnt, &eas.val);
 
                snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
 
@@ -195,7 +195,7 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt,
        case ECOMMUNITY_ENCODE_AS:
                eas.as = (*pnt++ << 8);
                eas.as |= (*pnt++);
-               pnt = ptr_get_be32(pnt, &eas.val);
+               ptr_get_be32(pnt, &eas.val);
 
                snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
 
@@ -3941,6 +3941,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
                vty_out(vty, "  L3-VNI: %u\n", bgp->l3vni);
                vty_out(vty, "  Rmac: %s\n",
                        prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
+               vty_out(vty, "  VNI Filter: %s\n",
+                       CHECK_FLAG(bgp->vrf_flags,
+                                  BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY) ?
+                               "prefix-routes-only" : "none");
                vty_out(vty, "  L2-VNI List:\n");
                vty_out(vty, "    ");
                for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
@@ -3966,6 +3970,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
                json_object_string_add(json, "rmac",
                                       prefix_mac2str(&bgp->rmac, buf,
                                                      sizeof(buf)));
+               json_object_string_add(json, "vniFilter",
+                                      CHECK_FLAG(bgp->vrf_flags,
+                                                 BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
+                                      ? "prefix-routes-only" : "none");
                /* list of l2vnis */
                for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
                        json_object_array_add(json_vnis,
index de453de0c82676328a60e024931f34ae66fdfcf3..de11a98a204fb3818419e88298801aacada12f95 100644 (file)
@@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer)
                /* First entry point of peer's finite state machine.  In Idle
                   status start timer is on unless peer is shutdown or peer is
                   inactive.  All other timer must be turned off */
-               if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)) {
+               if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)
+                   || peer->bgp->vrf_id == VRF_UNKNOWN) {
                        BGP_TIMER_OFF(peer->t_start);
                } else {
                        BGP_TIMER_ON(peer->t_start, bgp_start_timer,
@@ -1376,6 +1377,15 @@ int bgp_start(struct peer *peer)
                return 0;
        }
 
+       if (peer->bgp &&
+           peer->bgp->vrf_id == VRF_UNKNOWN) {
+               if (bgp_debug_neighbor_events(peer))
+                       zlog_err(
+                                "%s [FSM] In a VRF that is not initialised yet",
+                                peer->host);
+               return -1;
+       }
+
        /* Register to be notified on peer up */
        if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
            && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
index 5a48c7013e63d55425cc7d9d94e76abc3afc263f..1504893c472a105ac30f003bf352dc73811f1faf 100644 (file)
@@ -119,7 +119,7 @@ static void peer_process(struct hash_backet *hb, void *arg)
        }
 
        /* if calculated next update for this peer < current delay, use it */
-       if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <))
+       if (next_update->tv_sec < 0 || timercmp(&diff, next_update, <))
                *next_update = diff;
 }
 
index 0508f4846de40d5933d3cbe3ecb7984399c8e74a..82c74e4afa5b971e0531405790096cba255f19d2 100644 (file)
@@ -41,6 +41,7 @@
 #include "vrf.h"
 #include "bfd.h"
 #include "libfrr.h"
+#include "ns.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_attr.h"
@@ -57,6 +58,7 @@
 #include "bgpd/bgp_zebra.h"
 #include "bgpd/bgp_packet.h"
 #include "bgpd/bgp_keepalives.h"
+#include "bgpd/bgp_network.h"
 
 #ifdef ENABLE_BGP_VNC
 #include "bgpd/rfapi/rfapi_backend.h"
@@ -259,6 +261,7 @@ static int bgp_vrf_enable(struct vrf *vrf)
                /* We have instance configured, link to VRF and make it "up". */
                bgp_vrf_link(bgp, vrf);
 
+               bgp_handle_socket(bgp, vrf, old_vrf_id, true);
                /* Update any redistribute vrf bitmaps if the vrf_id changed */
                if (old_vrf_id != bgp->vrf_id)
                        bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id);
@@ -282,6 +285,7 @@ static int bgp_vrf_disable(struct vrf *vrf)
        bgp = bgp_lookup_by_name(vrf->name);
        if (bgp) {
                old_vrf_id = bgp->vrf_id;
+               bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
                /* We have instance configured, unlink from VRF and make it
                 * "down". */
                bgp_vrf_unlink(bgp, vrf);
index 9d32c4bee11d916937162ef007df83be9b957c49..5d83192a3022418f9787f6382bd42c3c503f0baa 100644 (file)
@@ -92,6 +92,26 @@ int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
        return 0;
 }
 
+/*
+ * bgp_interface_same
+ *
+ * Return true if ifindex for ifp1 and ifp2 are the same, else return false.
+ */
+static int bgp_interface_same(struct interface *ifp1, struct interface *ifp2)
+{
+       if (!ifp1 && !ifp2)
+               return 1;
+
+       if (!ifp1 && ifp2)
+               return 0;
+
+       if (ifp1 && !ifp2)
+               return 0;
+
+       return (ifp1->ifindex == ifp2->ifindex);
+}
+
+
 /*
  * bgp_info_nexthop_cmp
  *
@@ -130,7 +150,8 @@ int bgp_info_nexthop_cmp(struct bgp_info *bi1, struct bgp_info *bi2)
 
                                if (!bi1->attr->mp_nexthop_prefer_global &&
                                    !bi2->attr->mp_nexthop_prefer_global)
-                                       compare = !(bi1->peer->ifindex == bi2->peer->ifindex);
+                                       compare = !bgp_interface_same(bi1->peer->ifp, bi2->peer->ifp);
+
                                if (!compare)
                                        compare = IPV6_ADDR_CMP(&addr1, &addr2);
                                break;
index bf39cbe1fcf90fb47dc4023450a625a418349363..0ab583f44407deb557b3f2f71fceef1568ef3148 100644 (file)
@@ -34,6 +34,7 @@
 #include "queue.h"
 #include "hash.h"
 #include "filter.h"
+#include "ns.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_open.h"
 
 extern struct zebra_privs_t bgpd_privs;
 
-static int bgp_bind(struct peer *);
+static char *bgp_get_bound_name(struct peer *peer);
 
 /* BGP listening socket. */
 struct bgp_listener {
        int fd;
        union sockunion su;
        struct thread *thread;
+       struct bgp *bgp;
 };
 
 /*
@@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread)
                return -1;
        }
        listener->thread = NULL;
+
        thread_add_read(bm->master, bgp_accept, listener, accept_sock,
                        &listener->thread);
 
@@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread)
        }
        set_nonblocking(bgp_sock);
 
-       /* Obtain BGP instance this connection is meant for. */
-       if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
+       /* Obtain BGP instance this connection is meant for.
+        * - if it is a VRF netns sock, then BGP is in listener structure
+        * - otherwise, the bgp instance need to be demultiplexed
+        */
+       if (listener->bgp)
+               bgp = listener->bgp;
+       else if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
                if (bgp_debug_neighbor_events(NULL))
                        zlog_debug(
                                "[Event] Could not get instance for incoming conn from %s",
@@ -407,7 +415,7 @@ static int bgp_accept(struct thread *thread)
        peer->doppelganger = peer1;
        peer1->doppelganger = peer;
        peer->fd = bgp_sock;
-       bgp_bind(peer);
+       vrf_bind(peer->bgp->vrf_id, bgp_sock, bgp_get_bound_name(peer));
        bgp_fsm_change_status(peer, Active);
        BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */
 
@@ -435,21 +443,20 @@ static int bgp_accept(struct thread *thread)
 }
 
 /* BGP socket bind. */
-static int bgp_bind(struct peer *peer)
+static char *bgp_get_bound_name(struct peer *peer)
 {
-#ifdef SO_BINDTODEVICE
-       int ret;
-       int myerrno;
        char *name = NULL;
 
-       /* If not bound to an interface or part of a VRF, we don't care. */
-       if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if)
-               return 0;
+       if ((peer->bgp->vrf_id == VRF_DEFAULT) &&
+           !peer->ifname && !peer->conf_if)
+               return NULL;
 
        if (peer->su.sa.sa_family != AF_INET
            && peer->su.sa.sa_family != AF_INET6)
-               return 0; // unexpected
+               return NULL; // unexpected
 
+       if (!peer)
+               return name;
        /* For IPv6 peering, interface (unnumbered or link-local with interface)
         * takes precedence over VRF. For IPv4 peering, explicit interface or
         * VRF are the situations to bind.
@@ -461,30 +468,7 @@ static int bgp_bind(struct peer *peer)
        else
                name = peer->ifname ? peer->ifname : peer->bgp->name;
 
-       if (!name)
-               return 0;
-
-       if (bgp_debug_neighbor_events(peer))
-               zlog_debug("%s Binding to interface %s", peer->host, name);
-
-       if (bgpd_privs.change(ZPRIVS_RAISE))
-               zlog_err("bgp_bind: could not raise privs");
-
-       ret = setsockopt(peer->fd, SOL_SOCKET, SO_BINDTODEVICE, name,
-                        strlen(name));
-       myerrno = errno;
-
-       if (bgpd_privs.change(ZPRIVS_LOWER))
-               zlog_err("bgp_bind: could not lower privs");
-
-       if (ret < 0) {
-               if (bgp_debug_neighbor_events(peer))
-                       zlog_debug("bind to interface %s failed, errno=%d",
-                                  name, myerrno);
-               return ret;
-       }
-#endif /* SO_BINDTODEVICE */
-       return 0;
+       return name;
 }
 
 static int bgp_update_address(struct interface *ifp, const union sockunion *dst,
@@ -558,8 +542,13 @@ int bgp_connect(struct peer *peer)
                zlog_debug("Peer address not learnt: Returning from connect");
                return 0;
        }
+       if (bgpd_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
        /* Make socket for the peer. */
-       peer->fd = sockunion_socket(&peer->su);
+       peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id,
+                                       bgp_get_bound_name(peer));
+       if (bgpd_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
        if (peer->fd < 0)
                return -1;
 
@@ -591,9 +580,6 @@ int bgp_connect(struct peer *peer)
        if (peer->password)
                bgp_md5_set_connect(peer->fd, &peer->su, peer->password);
 
-       /* Bind socket. */
-       bgp_bind(peer);
-
        /* Update source bind. */
        if (bgp_update_source(peer) < 0) {
                return connect_error;
@@ -642,12 +628,12 @@ int bgp_getsockname(struct peer *peer)
                return -1;
 #endif
        }
-
        return 0;
 }
 
 
-static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
+static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen,
+                       struct bgp *bgp)
 {
        struct bgp_listener *listener;
        int ret, en;
@@ -683,8 +669,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
                return ret;
        }
 
-       listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
+       listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
        listener->fd = sock;
+
+       /* this socket needs a change of ns. record bgp back pointer */
+       if (bgp->vrf_id != VRF_DEFAULT &&
+           vrf_is_mapped_on_netns(bgp->vrf_id))
+               listener->bgp = bgp;
+
        memcpy(&listener->su, sa, salen);
        listener->thread = NULL;
        thread_add_read(bm->master, bgp_accept, listener, sock,
@@ -695,7 +687,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
 }
 
 /* IPv6 supported version of BGP server socket setup.  */
-int bgp_socket(unsigned short port, const char *address)
+int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)
 {
        struct addrinfo *ainfo;
        struct addrinfo *ainfo_save;
@@ -710,7 +702,12 @@ int bgp_socket(unsigned short port, const char *address)
        snprintf(port_str, sizeof(port_str), "%d", port);
        port_str[sizeof(port_str) - 1] = '\0';
 
-       ret = getaddrinfo(address, port_str, &req, &ainfo_save);
+       if (bgpd_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
+       ret = vrf_getaddrinfo(address, port_str, &req,
+                             &ainfo_save, bgp->vrf_id);
+       if (bgpd_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
        if (ret != 0) {
                zlog_err("getaddrinfo: %s", gai_strerror(ret));
                return -1;
@@ -723,8 +720,13 @@ int bgp_socket(unsigned short port, const char *address)
                if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
                        continue;
 
-               sock = socket(ainfo->ai_family, ainfo->ai_socktype,
-                             ainfo->ai_protocol);
+               if (bgpd_privs.change(ZPRIVS_RAISE))
+                       zlog_err("Can't raise privileges");
+               sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype,
+                                 ainfo->ai_protocol, bgp->vrf_id,
+                                 NULL);
+               if (bgpd_privs.change(ZPRIVS_LOWER))
+                       zlog_err("Can't lower privileges");
                if (sock < 0) {
                        zlog_err("socket: %s", safe_strerror(errno));
                        continue;
@@ -734,7 +736,8 @@ int bgp_socket(unsigned short port, const char *address)
                 * ttl=255 */
                sockopt_ttl(ainfo->ai_family, sock, MAXTTL);
 
-               ret = bgp_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen);
+               ret = bgp_listener(sock, ainfo->ai_addr,
+                                  ainfo->ai_addrlen, bgp);
                if (ret == 0)
                        ++count;
                else
@@ -751,6 +754,32 @@ int bgp_socket(unsigned short port, const char *address)
        return 0;
 }
 
+/* this function closes vrf socket
+ * this should be called only for vrf socket with netns backend
+ */
+void bgp_close_vrf_socket(struct bgp *bgp)
+{
+       struct listnode *node, *next;
+       struct bgp_listener *listener;
+
+       if (!bgp)
+               return;
+
+       if (bm->listen_sockets == NULL)
+               return;
+
+       for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
+               if (listener->bgp == bgp) {
+                       thread_cancel(listener->thread);
+                       close(listener->fd);
+                       listnode_delete(bm->listen_sockets, listener);
+                       XFREE(MTYPE_BGP_LISTENER, listener);
+               }
+       }
+}
+
+/* this function closes main socket
+ */
 void bgp_close(void)
 {
        struct listnode *node, *next;
@@ -760,6 +789,8 @@ void bgp_close(void)
                return;
 
        for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
+               if (listener->bgp)
+                       continue;
                thread_cancel(listener->thread);
                close(listener->fd);
                listnode_delete(bm->listen_sockets, listener);
index 75ff1305c24448f5455a2a1ce45efc6dd444cdd9..f18484e0009721605032fc3c4606e8f5414cf7bd 100644 (file)
@@ -23,7 +23,9 @@
 
 #define BGP_SOCKET_SNDBUF_SIZE 65536
 
-extern int bgp_socket(unsigned short, const char *);
+extern int bgp_socket(struct bgp *bgp, unsigned short port,
+                     const char *address);
+extern void bgp_close_vrf_socket(struct bgp *bgp);
 extern void bgp_close(void);
 extern int bgp_connect(struct peer *);
 extern int bgp_getsockname(struct peer *);
index 7ee641d6cf35f7403b7573754da44610d17a14e8..c7140b2f1f8b1d4d8b28f761c8a346250786b7c8 100644 (file)
@@ -3395,7 +3395,7 @@ DEFUN (no_neighbor_set_peer_group,
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       ret = peer_group_unbind(bgp, peer, group);
+       ret = peer_delete(peer);
 
        return bgp_vty_return(vty, ret);
 }
index 1dc08e2b008c0d57046a11a6470dba27996a6870..1da6136428a3beca98c23b71447d3fa307b638de 100644 (file)
@@ -1726,6 +1726,7 @@ static void bgp_zebra_connected(struct zclient *zclient)
 static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
                                         zebra_size_t length, vrf_id_t vrf_id)
 {
+       int filter = 0;
        char buf[ETHER_ADDR_STRLEN];
        vni_t l3vni = 0;
        struct ethaddr rmac;
@@ -1739,16 +1740,20 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
        if (cmd == ZEBRA_L3VNI_ADD) {
                stream_get(&rmac, s, sizeof(struct ethaddr));
                originator_ip.s_addr = stream_get_ipv4(s);
+               stream_get(&filter, s, sizeof(int));
        }
 
        if (BGP_DEBUG(zebra, ZEBRA))
-               zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s",
+               zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s filter %s",
                           (cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
-                          vrf_id_to_name(vrf_id), l3vni,
-                          prefix_mac2str(&rmac, buf, sizeof(buf)));
+                          vrf_id_to_name(vrf_id),
+                          l3vni,
+                          prefix_mac2str(&rmac, buf, sizeof(buf)),
+                          filter ? "prefix-routes-only" : "none");
 
        if (cmd == ZEBRA_L3VNI_ADD)
-               bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip);
+               bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip,
+                                        filter);
        else
                bgp_evpn_local_l3vni_del(l3vni, vrf_id);
 
index 78e748fb6c5d2c5765af9ee67f6121f8b838c9ee..63eff81145624a44c27304b8925b12b1b8cb71a5 100644 (file)
@@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp);
 
 extern struct zclient *zclient;
 
+/* handle main socket creation or deletion */
+static int bgp_check_main_socket(bool create, struct bgp *bgp)
+{
+       static int bgp_server_main_created;
+       struct listnode *bgpnode, *nbgpnode;
+       struct bgp *bgp_temp;
+
+       if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+               return 0;
+       if (create == true) {
+               if (bgp_server_main_created)
+                       return 0;
+               if (bgp_socket(bgp, bm->port, bm->address) < 0)
+                       return BGP_ERR_INVALID_VALUE;
+               bgp_server_main_created = 1;
+               return 0;
+       }
+       if (!bgp_server_main_created)
+               return 0;
+       /* only delete socket on some cases */
+       for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp_temp)) {
+               /* do not count with current bgp */
+               if (bgp_temp == bgp)
+                       continue;
+               /* if other instance non VRF, do not delete socket */
+               if (bgp_temp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+                       return 0;
+               /* vrf lite, do not delete socket */
+               if (!vrf_is_mapped_on_netns(bgp_temp->vrf_id))
+                       return 0;
+       }
+       bgp_close();
+       bgp_server_main_created = 0;
+       return 0;
+}
+
 void bgp_session_reset(struct peer *peer)
 {
        if (peer->doppelganger && (peer->doppelganger->status != Deleted)
@@ -1200,7 +1236,6 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
        peer_dst->config = peer_src->config;
 
        peer_dst->local_as = peer_src->local_as;
-       peer_dst->ifindex = peer_src->ifindex;
        peer_dst->port = peer_src->port;
        (void)peer_sort(peer_dst);
        peer_dst->rmap_type = peer_src->rmap_type;
@@ -2981,11 +3016,67 @@ struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id)
        return (vrf->info) ? (struct bgp *)vrf->info : NULL;
 }
 
+/* handle socket creation or deletion, if necessary
+ * this is called for all new BGP instances
+ */
+int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf,
+                         vrf_id_t old_vrf_id, bool create)
+{
+       int ret = 0;
+
+       /* Create BGP server socket, if listen mode not disabled */
+       if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
+               return 0;
+       if (bgp->name
+           && bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+           && vrf) {
+               /*
+                * suppress vrf socket
+                */
+               if (create == FALSE) {
+                       if (vrf_is_mapped_on_netns(vrf->vrf_id))
+                               bgp_close_vrf_socket(bgp);
+                       else
+                               ret = bgp_check_main_socket(create, bgp);
+                       return ret;
+               }
+               /* do nothing
+                * if vrf_id did not change
+                */
+               if (vrf->vrf_id == old_vrf_id)
+                       return 0;
+               if (old_vrf_id != VRF_UNKNOWN) {
+                       /* look for old socket. close it. */
+                       bgp_close_vrf_socket(bgp);
+               }
+               /* if backend is not yet identified ( VRF_UNKNOWN) then
+                *   creation will be done later
+                */
+               if (vrf->vrf_id == VRF_UNKNOWN)
+                       return 0;
+               /* if BGP VRF instance requested
+                * if backend is NETNS, create BGP server socket in the NETNS
+                */
+               if (vrf_is_mapped_on_netns(bgp->vrf_id)) {
+                       ret = bgp_socket(bgp, bm->port, bm->address);
+                       if (ret < 0)
+                               return BGP_ERR_INVALID_VALUE;
+                       return 0;
+               }
+       }
+       /* if BGP VRF instance requested or VRF lite backend
+        * if BGP non VRF instance, create it
+        *  if not already done
+        */
+       return bgp_check_main_socket(create, bgp);
+}
+
 /* Called from VTY commands. */
 int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
            enum bgp_instance_type inst_type)
 {
        struct bgp *bgp;
+       struct vrf *vrf = NULL;
 
        /* Multiple instance check. */
        if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) {
@@ -3033,25 +3124,19 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
 
        bgp->t_rmap_def_originate_eval = NULL;
 
-       /* Create BGP server socket, if first instance.  */
-       if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) {
-               if (bgp_socket(bm->port, bm->address) < 0)
-                       return BGP_ERR_INVALID_VALUE;
-       }
-
-       listnode_add(bm->bgp, bgp);
-
        /* If Default instance or VRF, link to the VRF structure, if present. */
        if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
            || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
-               struct vrf *vrf;
-
                vrf = bgp_vrf_lookup_by_instance_type(bgp);
                if (vrf)
                        bgp_vrf_link(bgp, vrf);
        }
+       /* BGP server socket already processed if BGP instance
+        * already part of the list
+        */
+       bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true);
+       listnode_add(bm->bgp, bgp);
 
-       /* Register with Zebra, if needed */
        if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
                bgp_zebra_instance_register(bgp);
 
@@ -3188,8 +3273,6 @@ int bgp_delete(struct bgp *bgp)
         * routes to be processed still referencing the struct bgp.
         */
        listnode_delete(bm->bgp, bgp);
-       if (list_isempty(bm->bgp))
-               bgp_close();
 
        /* Deregister from Zebra, if needed */
        if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
@@ -3199,6 +3282,7 @@ int bgp_delete(struct bgp *bgp)
        bgp_if_finish(bgp);
 
        vrf = bgp_vrf_lookup_by_instance_type(bgp);
+       bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
        if (vrf)
                bgp_vrf_unlink(bgp, vrf);
 
@@ -3337,11 +3421,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
                struct listnode *bgpnode, *nbgpnode;
 
                for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
-                       /* Skip VRFs, this function will not be invoked without
-                        * an instance
+                       /* Skip VRFs Lite only, this function will not be
+                        * invoked without an instance
                         * when examining VRFs.
                         */
-                       if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+                       if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) &&
+                           !vrf_is_mapped_on_netns(bgp->vrf_id))
                                continue;
 
                        peer = hash_lookup(bgp->peerhash, &tmp_peer);
index 220b6d989e43b551f98b8275d4884b85f7c6c14d..b3c7418602c73b4e73c2ca11521e3377a0c5cb7e 100644 (file)
@@ -436,6 +436,7 @@ struct bgp {
 #define BGP_VRF_IMPORT_RT_CFGD              (1 << 3)
 #define BGP_VRF_EXPORT_RT_CFGD              (1 << 4)
 #define BGP_VRF_RD_CFGD                     (1 << 5)
+#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY    (1 << 6)
 
        /* unique ID for auto derivation of RD for this vrf */
        uint16_t vrf_rd_id;
@@ -686,7 +687,6 @@ struct peer {
        time_t readtime;     /* Last read time */
        time_t resettime;    /* Last reset time */
 
-       ifindex_t ifindex;     /* ifindex of the BGP connection. */
        char *conf_if;   /* neighbor interface config name. */
        struct interface *ifp; /* corresponding interface */
        char *ifname;     /* bind interface name. */
@@ -1354,6 +1354,9 @@ extern void bgp_instance_up(struct bgp *);
 extern void bgp_instance_down(struct bgp *);
 extern int bgp_delete(struct bgp *);
 
+extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf,
+                            vrf_id_t old_vrf_id, bool create);
+
 extern int bgp_flag_set(struct bgp *, int);
 extern int bgp_flag_unset(struct bgp *, int);
 extern int bgp_flag_check(struct bgp *, int);
index 6bd2d44fe864475545210f4c926982ff11ccef90..0d6c99acfa64b8f2f61b3310a81b4acb765fcf4e 100755 (executable)
@@ -1886,6 +1886,7 @@ AC_CONFIG_FILES([Makefile
          doc/eigrpd.8
          doc/ripngd.8
          doc/pimd.8
+         doc/mtracebis.8
          doc/nhrpd.8
          doc/vtysh.1
          doc/watchfrr.8
index adce915e1f075d7083f7c51a8669184efd25c7f1..8e83bdf6df8a644c455b15517232df87699e24b7 100644 (file)
@@ -1,5 +1,6 @@
 etc/frr/
 usr/bin/vtysh
+usr/bin/mtracebis
 usr/include/frr/
 usr/lib/
 tools/frr etc/init.d/
@@ -15,6 +16,7 @@ usr/share/man/man8/ripngd.8
 usr/share/man/man8/zebra.8
 usr/share/man/man8/isisd.8
 usr/share/man/man8/watchfrr.8
+usr/share/man/man8/mtracebis.8
 usr/share/snmp/mibs/
 tools/etc/* etc/
 tools/*.service    lib/systemd/system
index 2d86009dba8a1b47354a1c91ed8c51ee2ea2db23..1aee4d540e8868b7f41cd86f203fae61efc63089 100644 (file)
@@ -1,5 +1,6 @@
 etc/frr/
 usr/bin/vtysh
+usr/bin/mtracebis
 usr/include/frr/
 usr/lib/
 tools/frr usr/lib/frr
@@ -16,6 +17,7 @@ usr/share/man/man8/zebra.8
 usr/share/man/man8/isisd.8
 usr/share/man/man8/watchfrr.8
 usr/share/man/man8/frr-args.8
+usr/share/man/man8/mtracebis.8
 usr/share/snmp/mibs/
 tools/etc/* etc/
 tools/*.service    lib/systemd/system
index 787b80fbf7dd019e29341a14be50c29aa2a7d493..38802483b6f04852cc1a419da37863551676cd5a 100644 (file)
@@ -20,7 +20,7 @@ Add packages:
     sudo yum install git autoconf automake libtool make gawk \
       readline-devel texinfo net-snmp-devel groff pkgconfig \
       json-c-devel pam-devel bison flex pytest c-ares-devel \
-      perl-XML-LibXML python-devel
+      perl-XML-LibXML python-devel systemd-devel
 
 Get FRR, compile it and install it (from Git)
 ---------------------------------------------
@@ -59,6 +59,7 @@ an example.)
         --enable-group=frr \
         --enable-vty-group=frrvt \
         --enable-rtadv \
+       --enable-systemd=yes \
         --disable-exampledir \
         --enable-watchfrr \
         --disable-ldpd \
index 7aaa36556f25da4647ba0f0ed1a8552f469d5a74..9c81202db9e7938500a0fcabf6d5888fb48c4516 100644 (file)
@@ -104,6 +104,7 @@ man_MANS = frr.1 frr-args.8
 
 if PIMD
 man_MANS += pimd.8
+man_MANS += mtracebis.8
 endif
 
 if BGPD
@@ -169,6 +170,7 @@ EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \
        ripd.8.in \
        ripngd.8.in \
        pimd.8.in \
+       mtracebis.8.in \
        nhrpd.8.in \
        vtysh.1.in \
        watchfrr.8.in \
diff --git a/doc/mtracebis.8.in b/doc/mtracebis.8.in
new file mode 100644 (file)
index 0000000..3abc717
--- /dev/null
@@ -0,0 +1,25 @@
+.\" This file was originally generated by help2man 1.44.1.
+.TH MTRACEBIS "8" "February 2018" "mtracebis 0.1" "System Administration Utilities"
+.SH NAME
+mtracebis \- a program to initiate multicast traceroute "mtrace" queries
+.SH SYNOPSIS
+mtracebis
+<multicast source>
+.SH DESCRIPTION
+.B mtracebis
+is a program used to test multicast connectivity in a multicast and multicast
+traceroute enabled IP network.
+.PP
+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. 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.
+.PP
+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.
+.SH SEE ALSO
+See the project homepage at <@PACKAGE_URL@>.
+.SH AUTHORS
+Copyright 2018 Mladen Sablic
index 7f4a81b1a0b2c55d86b54fc8edbc6c29340e99c8..3e1d10e068ac5e1eaaa9d747b1228c6ad474b5e8 100644 (file)
@@ -92,6 +92,11 @@ maximum before starting zebra.
 
 Note that this affects Linux only.
 .TP
+\fB\-n\fR, \fB\-\-vrfwnetns \fR\fIEnable namespace VRF\fR
+Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite
+support from Linux devices. This option permits discovering using Linux named
+Netns and map it to FRR VRF contexts.
+.TP
 \fB\-M\fR, \fB\-\-module \fR\fImodule:options\fR
 Load a module at startup.  May be specified more than once.
 The \fBsnmp\fR and \fBfpm\fR modules may be
index 3759c6414838c8cba7570c4d0ca1e54dc05011ca..e8392f50b4269a0af3c9b9f6ecb274a243bc5e3f 100644 (file)
@@ -94,13 +94,14 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient,
        return 0;
 }
 
-static int eigrp_zebra_notify_owner(int command, struct zclient *zclient,
-                                   zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient,
+                                         zebra_size_t length, vrf_id_t vrf_id)
 {
        struct prefix p;
        enum zapi_route_notify_owner note;
+       uint32_t table;
 
-       if (!zapi_route_notify_decode(zclient->ibuf, &p, &note))
+       if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note))
                return -1;
 
        return 0;
@@ -128,7 +129,7 @@ void eigrp_zebra_init(void)
        zclient->interface_address_delete = eigrp_interface_address_delete;
        zclient->redistribute_route_add = eigrp_zebra_read_route;
        zclient->redistribute_route_del = eigrp_zebra_read_route;
-       zclient->notify_owner = eigrp_zebra_notify_owner;
+       zclient->route_notify_owner = eigrp_zebra_route_notify_owner;
 }
 
 
diff --git a/git-reindent-branch.py b/git-reindent-branch.py
deleted file mode 100644 (file)
index c207f59..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import sys, os
-import subprocess, argparse, tempfile
-import indent
-
-def run(cmd):
-    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE)
-    rv = proc.communicate('')[0].decode('UTF-8')
-    proc.wait()
-    return rv
-
-clangfmt = run(['git', 'show', 'master:.clang-format'])
-
-argp = argparse.ArgumentParser(description = 'git whitespace-fixing tool')
-argp.add_argument('branch', metavar='BRANCH', type = str, nargs = '?', default = 'HEAD')
-args = argp.parse_args()
-
-branch = args.branch
-commit   = run(['git', 'rev-list', '-n', '1', branch, '--']).strip()
-
-# frr-3.1-dev = first commit that is on master but not on stable/3.0
-masterid = run(['git', 'rev-list', '-n', '1', 'frr-3.1-dev', '--']).strip()
-masterbase = run(['git', 'merge-base', commit, masterid]).strip()
-
-if masterbase == masterid:
-    refbranch = 'master'
-else:
-    refbranch = '3.0'
-
-sys.stderr.write('autodetected base: %s (can be 3.0 or master)\n' % refbranch)
-
-beforeid = run(['git', 'rev-list', '-n', '1', 'reindent-%s-before' % refbranch, '--']).strip()
-afterid  = run(['git', 'rev-list', '-n', '1', 'reindent-%s-after' % refbranch, '--']).strip()
-
-beforebase = run(['git', 'merge-base', commit, beforeid]).strip()
-afterbase  = run(['git', 'merge-base', commit, afterid]).strip()
-
-if afterbase == afterid:
-    sys.stderr.write('this branch was already rebased\n')
-    sys.exit(1)
-
-if beforebase != beforeid:
-    sys.stderr.write('you need to rebase your branch onto the tag "reindent-%s-before"\n' % refbranch)
-    sys.exit(1)
-
-revs = run(['git', 'rev-list', 'reindent-%s-before..%s' % (refbranch, commit)]).strip().split('\n')
-revs.reverse()
-
-srcdir = os.getcwd()
-tmpdir = tempfile.mkdtemp('frrindent')
-os.chdir(tmpdir)
-
-sys.stderr.write('using temporary directory %s; %d revisions\n' % (tmpdir, len(revs)))
-run(['git', 'clone', '-s', '-b', 'reindent-%s-after' % refbranch, srcdir, 'repo'])
-os.chdir('repo')
-
-with open('.clang-format', 'w') as fd:
-    fd.write(clangfmt)
-
-prev = beforeid
-for rev in revs:
-    filestat = run(['git', 'diff', '-z', '--name-status', prev, rev]).rstrip('\0').split('\0')
-    changes = zip(filestat[0::2], filestat[1::2])
-    sys.stderr.write('%s: %d files\n' % (rev, len(changes)))
-
-    for typ, name in changes:
-        if typ == 'D':
-            run(['git', 'rm', name])
-        elif typ in ['A', 'M']:
-            run(['git', 'checkout', rev, '--', name])
-            if name.endswith('.c') or name.endswith('.h'):
-                for d in ['babeld/', 'ldpd/', 'nhrpd/']:
-                    if name.startswith(d):
-                        break
-                else:
-                    sys.stderr.write('\t%s\n' % name)
-                    indent.wrap_file(name)
-            run(['git', 'add', name])
-
-    run(['git', 'commit', '-C', rev])
-    prev = rev
-
-run(['git', 'push', 'origin', 'HEAD:refs/heads/reindented-branch'])
-sys.stderr.write('\n\n"reindented-branch" should now be OK.\n')
-sys.stderr.write('you could use "git reset --hard reindented-branch" to set your current branch to the reindented output\n')
-sys.stderr.write('\033[31;1mplease always double-check the output\033[m\n')
-
diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h
new file mode 100644 (file)
index 0000000..9a92b7e
--- /dev/null
@@ -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_ */
index 98bd14800222ffb1243da7bd96262365477665be..7a12b2ffae990410842e78cdeee675b5f0b17c78 100644 (file)
@@ -6,4 +6,5 @@ noinst_HEADERS += \
        include/linux/neighbour.h \
        include/linux/rtnetlink.h \
        include/linux/socket.h \
+       include/linux/net_namespace.h \
        # end
diff --git a/indent.py b/indent.py
deleted file mode 100644 (file)
index 560c13c..0000000
--- a/indent.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# 2017 by David Lamparter, placed in public domain
-
-import sys, re, subprocess, os
-
-# find all DEFUNs
-defun_re = re.compile(
-        r'^((DEF(UN(_NOSH|_HIDDEN)?|PY)|ALIAS)\s*\(.*?)^(?=\s*\{)',
-        re.M | re.S)
-define_re = re.compile(
-        r'((^#\s*define[^\n]+[^\\]\n)+)',
-        re.M | re.S)
-# find clang-format control that we just inserted
-clean_re = re.compile(
-        r'^/\* \$FRR indent\$ \*/\s*\n\s*/\* clang-format (on|off) \*/\s*\n',
-        re.M)
-
-def wrap_file(fn):
-    with open(fn, 'r') as fd:
-        text = fd.read()
-
-        repl = r'/* $FRR indent$ */\n/* clang-format off */\n' + \
-                r'\1' + \
-                r'/* $FRR indent$ */\n/* clang-format on */\n'
-
-        # around each DEFUN, insert an indent-on/off comment
-        text = defun_re.sub(repl, text)
-        text = define_re.sub(repl, text)
-
-        ci = subprocess.Popen(['clang-format'], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
-        stdout, ign = ci.communicate(text)
-        ci.wait()
-        if ci.returncode != 0:
-            raise IOError('clang-format returned %d' % (ci.returncode))
-
-        # remove the bits we inserted above
-        final = clean_re.sub('', stdout)
-
-        tmpname = fn + '.indent'
-        with open(tmpname, 'w') as ofd:
-            ofd.write(final)
-        os.rename(tmpname, fn)
-
-if __name__ == '__main__':
-    for fn in sys.argv[1:]:
-        wrap_file(fn)
index 98087942a670bcddfb03d8462da30cb3c59f4955..20ce0f1fade63f7ba8c806240f170ffdb5730c10 100644 (file)
@@ -1168,7 +1168,6 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
        struct isis_area *area = circuit->area;
        bool change = circuit->ip_router != ip_router
                      || circuit->ipv6_router != ipv6_router;
-       bool was_enabled = !!circuit->area;
 
        area->ip_circuits += ip_router - circuit->ip_router;
        area->ipv6_circuits += ipv6_router - circuit->ipv6_router;
@@ -1182,8 +1181,6 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
 
        if (!ip_router && !ipv6_router)
                isis_csm_state_change(ISIS_DISABLE, circuit, area);
-       else if (!was_enabled)
-               isis_csm_state_change(ISIS_ENABLE, circuit, area);
        else
                lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
 }
index bbcea9f553495904a1513279b8a9e750bbe29538..b25be43a5c398f244d15589403a2d723fa97f343 100644 (file)
@@ -306,8 +306,11 @@ if_reset(struct iface *iface, int af)
        ia = iface_af_get(iface, af);
        if_stop_hello_timer(ia);
 
-       while ((adj = RB_ROOT(ia_adj_head, &ia->adj_tree)) != NULL)
+       while (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) {
+               adj = RB_ROOT(ia_adj_head, &ia->adj_tree);
+
                adj_del(adj, S_SHUTDOWN);
+       }
 
        /* try to cleanup */
        switch (af) {
index f638d6a65bbbbf4ef064272214603230ead2fcdb..1cfeae30927315d747ab027fbf70b21d668ec85b 100644 (file)
@@ -76,16 +76,21 @@ l2vpn_del(struct l2vpn *l2vpn)
        struct l2vpn_if         *lif;
        struct l2vpn_pw         *pw;
 
-       while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+               lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
                RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
                free(lif);
        }
-       while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+               pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
                RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
                free(pw);
        }
-       while ((pw = RB_ROOT(l2vpn_pw_head,
-           &l2vpn->pw_inactive_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+               pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
                RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
                free(pw);
        }
index a70b97d06b129c6b86fa2394ba235827af6f8c6c..5aa53fd39e01dbec2653b13dc513417b671719d1 100644 (file)
@@ -1324,8 +1324,11 @@ lde_nbr_clear(void)
 {
        struct lde_nbr  *ln;
 
-        while ((ln = RB_ROOT(nbr_tree, &lde_nbrs)) != NULL)
+       while (!RB_EMPTY(nbr_tree, &lde_nbrs)) {
+               ln = RB_ROOT(nbr_tree, &lde_nbrs);
+
                lde_nbr_del(ln);
+       }
 }
 
 static void
index 18c8c0a1229a31894ca97e5aef71015affa10500..28e455c7a58fc7009c64e6b96a0d9f76ed4d872d 100644 (file)
@@ -129,7 +129,9 @@ fec_clear(struct fec_tree *fh, void (*free_cb)(void *))
 {
        struct fec      *f;
 
-       while ((f = RB_ROOT(fec_tree, fh)) != NULL) {
+       while (!RB_EMPTY(fec_tree, fh)) {
+               f = RB_ROOT(fec_tree, fh);
+
                fec_remove(fh, f);
                free_cb(f);
        }
index 76c602afbb126b6ed05662d45d1cc3658f6880af..382b00688440d854285b66b10e0bd75479f5b87a 100644 (file)
@@ -1475,18 +1475,23 @@ l2vpn_del_api(struct ldpd_conf *conf, struct l2vpn *l2vpn)
        struct l2vpn_if         *lif;
        struct l2vpn_pw         *pw;
 
-       while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+               lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
                QOBJ_UNREG(lif);
                RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
                free(lif);
        }
-       while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+               pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
                QOBJ_UNREG(pw);
                RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
                free(pw);
        }
-       while ((pw = RB_ROOT(l2vpn_pw_head,
-           &l2vpn->pw_inactive_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+               pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
                QOBJ_UNREG(pw);
                RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
                free(pw);
index 12aeb1fff32afaa46f01a6b685fe9833e5dc4c0e..255febeb60344b676093a3eb6ad561e665940456 100644 (file)
@@ -1066,13 +1066,17 @@ ldp_config_reset_main(struct ldpd_conf *conf)
        struct iface            *iface;
        struct nbr_params       *nbrp;
 
-       while ((iface = RB_ROOT(iface_head, &conf->iface_tree)) != NULL) {
+       while (!RB_EMPTY(iface_head, &conf->iface_tree)) {
+               iface = RB_ROOT(iface_head, &conf->iface_tree);
+
                QOBJ_UNREG(iface);
                RB_REMOVE(iface_head, &conf->iface_tree, iface);
                free(iface);
        }
 
-       while ((nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree)) != NULL) {
+       while (!RB_EMPTY(nbrp_head, &conf->nbrp_tree)) {
+               nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree);
+
                QOBJ_UNREG(nbrp);
                RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
                free(nbrp);
@@ -1128,20 +1132,25 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf)
        struct l2vpn_if         *lif;
        struct l2vpn_pw         *pw;
 
-       while ((l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree)) != NULL) {
-               while ((lif = RB_ROOT(l2vpn_if_head,
-                   &l2vpn->if_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_head, &conf->l2vpn_tree)) {
+               l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree);
+               while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+                       lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
                        QOBJ_UNREG(lif);
                        RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
                        free(lif);
                }
-               while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
+               while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+                       pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
                        QOBJ_UNREG(pw);
                        RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
                        free(pw);
                }
-               while ((pw = RB_ROOT(l2vpn_pw_head,
-                   &l2vpn->pw_inactive_tree)) != NULL) {
+               while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+                       pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
                        QOBJ_UNREG(pw);
                        RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
                        free(pw);
@@ -1160,19 +1169,27 @@ ldp_clear_config(struct ldpd_conf *xconf)
        struct nbr_params       *nbrp;
        struct l2vpn            *l2vpn;
 
-       while ((iface = RB_ROOT(iface_head, &xconf->iface_tree)) != NULL) {
+       while (!RB_EMPTY(iface_head, &xconf->iface_tree)) {
+               iface = RB_ROOT(iface_head, &xconf->iface_tree);
+
                RB_REMOVE(iface_head, &xconf->iface_tree, iface);
                free(iface);
        }
-       while ((tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree)) != NULL) {
+       while (!RB_EMPTY(tnbr_head, &xconf->tnbr_tree)) {
+               tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree);
+
                RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr);
                free(tnbr);
        }
-       while ((nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree)) != NULL) {
+       while (!RB_EMPTY(nbrp_head, &xconf->nbrp_tree)) {
+               nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree);
+
                RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp);
                free(nbrp);
        }
-       while ((l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree)) != NULL) {
+       while (!RB_EMPTY(l2vpn_head, &xconf->l2vpn_tree)) {
+               l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree);
+
                RB_REMOVE(l2vpn_head, &xconf->l2vpn_tree, l2vpn);
                l2vpn_del(l2vpn);
        }
index 9d00bcd2b6a40eab0514eb6ff98ab38808902b8f..56af76d94eba7aa640f8b48497b9eced5c48bbb3 100644 (file)
@@ -219,8 +219,11 @@ ldpe_shutdown(void)
                assert(if_addr != LIST_FIRST(&global.addr_list));
                free(if_addr);
        }
-       while ((adj = RB_ROOT(global_adj_head, &global.adj_tree)) != NULL)
+       while (!RB_EMPTY(global_adj_head, &global.adj_tree)) {
+               adj = RB_ROOT(global_adj_head, &global.adj_tree);
+
                adj_del(adj, S_SHUTDOWN);
+       }
 
        /* clean up */
        if (iev_lde)
index 39860a185981d0492ed88466d60f2e176a249ce6..1c3f650dff3baede6f9fb730b862d3603106f1ad 100644 (file)
@@ -316,7 +316,9 @@ nbr_del(struct nbr *nbr)
        mapping_list_clr(&nbr->release_list);
        mapping_list_clr(&nbr->abortreq_list);
 
-       while ((adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree)) != NULL) {
+       while (!RB_EMPTY(nbr_adj_head, &nbr->adj_tree)) {
+               adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree);
+
                adj->nbr = NULL;
                RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
        }
index d17f2c3d48cb99bce0a48420cad0ecb4b9c4db43..10996d5dd4b80dfbd800662e5defc80ae255bf1c 100644 (file)
@@ -62,7 +62,7 @@ const char *node_names[] = {
        "aaa",                      // AAA_NODE,
        "keychain",                 // KEYCHAIN_NODE,
        "keychain key",             // KEYCHAIN_KEY_NODE,
-       "logical-router",           // NS_NODE,
+       "logical-router",           // LOGICALROUTER_NODE,
        "vrf",                      // VRF_NODE,
        "interface",                // INTERFACE_NODE,
        "zebra",                    // ZEBRA_NODE,
@@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty)
                break;
        case INTERFACE_NODE:
        case PW_NODE:
-       case NS_NODE:
+       case LOGICALROUTER_NODE:
        case VRF_NODE:
        case ZEBRA_NODE:
        case BGP_NODE:
@@ -1376,7 +1376,7 @@ DEFUN (config_end,
        case CONFIG_NODE:
        case INTERFACE_NODE:
        case PW_NODE:
-       case NS_NODE:
+       case LOGICALROUTER_NODE:
        case VRF_NODE:
        case ZEBRA_NODE:
        case RIP_NODE:
index e1edc1ef321901001acf80a271600692b5e19017..269318989f98b3f70a7ce8f3915994bdd85023d8 100644 (file)
@@ -85,7 +85,7 @@ enum node_type {
        AAA_NODE,               /* AAA node. */
        KEYCHAIN_NODE,          /* Key-chain node. */
        KEYCHAIN_KEY_NODE,      /* Key-chain key node. */
-       NS_NODE,                /* Logical-Router node. */
+       LOGICALROUTER_NODE,     /* Logical-Router node. */
        VRF_NODE,               /* VRF mode node. */
        INTERFACE_NODE,         /* Interface mode node. */
        ZEBRA_NODE,             /* zebra connection node. */
index 7866ddb8c446ffa08711d59dc4ce7b1b69c87e32..3a83de46ae9743e1cb04dc7f3564c0d5096d591b 100644 (file)
--- a/lib/if.c
+++ b/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;
 
+       ifp = if_lookup_by_name(name, vrf_id);
+       if (ifp)
+               return ifp;
+       /* Not Found on same VRF. If the interface command
+        * was entered in vty without a VRF (passed as VRF_DEFAULT),
+        * accept the ifp we found. If a vrf was entered and there is
+        * a mismatch, reject it if from vty.
+        */
        ifp = if_lookup_by_name_all_vrf(name);
-       if (ifp) {
-               if (ifp->vrf_id == vrf_id)
+       if (!ifp)
+               return if_create(name, vrf_id);
+       if (vty) {
+               if (vrf_id == VRF_DEFAULT)
                        return ifp;
-
-               /* Found a match on a different VRF. If the interface command
-                * was entered in vty without a VRF (passed as VRF_DEFAULT),
-                * accept the ifp we found. If a vrf was entered and there is
-                * a mismatch, reject it if from vty. If it came from the kernel
-                * or by way of zclient, believe it and update the ifp
-                * accordingly.
-                */
-               if (vty) {
-                       if (vrf_id == VRF_DEFAULT)
-                               return ifp;
-                       return NULL;
-               } else {
-                       if_update_to_new_vrf(ifp, vrf_id);
-                       return ifp;
-               }
+               return NULL;
        }
-
-       return if_create(name, vrf_id);
+       /* if vrf backend uses NETNS, then
+        * this should not be considered as an update
+        * then create the new interface
+        */
+       if (ifp->vrf_id != vrf_id &&
+           vrf_is_mapped_on_netns(vrf_id))
+               return if_create(name, vrf_id);
+       /* If it came from the kernel
+        * or by way of zclient, believe it and update
+        * the ifp accordingly.
+        */
+       if_update_to_new_vrf(ifp, vrf_id);
+       return ifp;
 }
 
 void if_set_index(struct interface *ifp, ifindex_t ifindex)
@@ -1064,7 +1070,7 @@ ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex)
       rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
       if (! rn)
        return NULL;
-      
+
       ifp = rn->info;
       route_unlock_node (rn);
       return ifp;
@@ -1078,7 +1084,9 @@ void if_terminate(struct vrf *vrf)
 {
        struct interface *ifp;
 
-       while ((ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name)) != NULL) {
+       while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
+               ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name);
+
                if (ifp->node) {
                        ifp->node->info = NULL;
                        route_unlock_node(ifp->node);
diff --git a/lib/logicalrouter.c b/lib/logicalrouter.c
new file mode 100644 (file)
index 0000000..4dc99d3
--- /dev/null
@@ -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;
+}
diff --git a/lib/logicalrouter.h b/lib/logicalrouter.h
new file mode 100644 (file)
index 0000000..5a0780c
--- /dev/null
@@ -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*/
diff --git a/lib/netns_linux.c b/lib/netns_linux.c
new file mode 100644 (file)
index 0000000..0e955ba
--- /dev/null
@@ -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;
+}
+
diff --git a/lib/netns_other.c b/lib/netns_other.c
new file mode 100644 (file)
index 0000000..2402dd1
--- /dev/null
@@ -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 */
diff --git a/lib/ns.c b/lib/ns.c
deleted file mode 100644 (file)
index fdf93d0..0000000
--- a/lib/ns.c
+++ /dev/null
@@ -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;
-}
index 79b4cab04d03c8bb5e3b2d187f2ca2c0faf43b5f..83e5e1b907895941be84abaab517e673a5bce4b3 100644 (file)
--- a/lib/ns.h
+++ b/lib/ns.h
 
 #include "openbsd-tree.h"
 #include "linklist.h"
+#include "vty.h"
 
 typedef u_int32_t ns_id_t;
 
 /* the default NS ID */
-#define NS_DEFAULT 0
 #define NS_UNKNOWN UINT32_MAX
 
 /* Default netns directory (Linux) */
 #define NS_RUN_DIR         "/var/run/netns"
 
+#ifdef HAVE_NETNS
+#define NS_DEFAULT_NAME    "/proc/self/ns/net"
+#else  /* !HAVE_NETNS */
+#define NS_DEFAULT_NAME    "Default-logical-router"
+#endif /* HAVE_NETNS */
+
 struct ns {
        RB_ENTRY(ns) entry;
 
@@ -49,6 +55,9 @@ struct ns {
        /* Master list of interfaces belonging to this NS */
        struct list *iflist;
 
+       /* Back Pointer to VRF */
+       void *vrf_ctxt;
+
        /* User data */
        void *info;
 };
@@ -57,6 +66,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare)
 
 extern struct ns_head ns_tree;
 
+/*
+ * API for managing NETNS. eg from zebra daemon
+ * one want to manage the list of NETNS, etc...
+ */
+
 /*
  * NS hooks
  */
@@ -74,20 +88,82 @@ extern struct ns_head ns_tree;
  *          - param 2: the address of the user data pointer (the user data
  *                     can be stored in or freed from there)
  */
-extern void ns_add_hook(int, int (*)(ns_id_t, void **));
+extern void ns_add_hook(int type, int (*)(struct ns *));
+
 
 /*
  * NS initializer/destructor
  */
-/* Please add hooks before calling ns_init(). */
-extern void ns_init(void);
+
 extern void ns_terminate(void);
 
+/* API to initialize NETNS managerment
+ * parameter is the default ns_id
+ */
+extern void ns_init_management(ns_id_t ns_id);
+
+
 /*
  * NS utilities
  */
 
-/* Create a socket serving for the given NS */
-extern int ns_socket(int, int, int, ns_id_t);
+/* Create a socket serving for the given NS
+ */
+int ns_socket(int domain, int type, int protocol, ns_id_t ns_id);
+
+/* return the path of the NETNS */
+extern char *ns_netns_pathname(struct vty *vty, const char *name);
+
+/* Parse and execute a function on all the NETNS */
+extern void ns_walk_func(int (*func)(struct ns *));
+
+/* API to get the NETNS name, from the ns pointer */
+extern const char *ns_get_name(struct ns *ns);
+
+/* only called from vrf ( when removing netns from vrf)
+ * or at VRF or logical router termination
+ */
+extern void ns_delete(struct ns *ns);
+
+/* return > 0 if netns is available
+ * called by VRF to check netns backend is available for VRF
+ */
+extern int ns_have_netns(void);
+
+/* API to get context information of a NS */
+extern void *ns_info_lookup(ns_id_t ns_id);
+
+/*
+ * NS init routine
+ * should be called from backendx
+ */
+extern void ns_init(void);
+
+/* API to retrieve default NS */
+extern ns_id_t ns_get_default_id(void);
+
+#define NS_DEFAULT ns_get_default_id()
+
+/* API that can be used to change from NS */
+extern int ns_switchback_to_initial(void);
+extern int ns_switch_to_netns(const char *netns_name);
+
+/*
+ * NS handling routines.
+ * called by modules that use NS backend
+ */
+
+/* API to search for already present NETNS */
+extern struct ns *ns_lookup(ns_id_t ns_id);
+extern struct ns *ns_lookup_name(const char *name);
+
+/* API to handle NS : creation, enable, disable
+ * for enable, a callback function is passed as parameter
+ * the callback belongs to the module that uses NS as backend
+ * upon enabling the NETNS, the upper layer is informed
+ */
+extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *));
+extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id);
+extern void ns_disable(struct ns *ns);
 
 #endif /*_ZEBRA_NS_H*/
index bcc2230607802c25d826cacc88d4db56d3b4db37..5bf7d498c1ca7073e8f34b1a8870e0c332c2563b 100644 (file)
@@ -239,6 +239,7 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
 #define IPV4_NET127(a)  ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
 #define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
 #define IPV4_CLASS_DE(a)  ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
+#define IPV4_MC_LINKLOCAL(a)  ((((u_int32_t) (a)) & 0xffffff00) == 0xe0000000)
 
 /* Max bit/byte length of IPv6 address. */
 #define IPV6_MAX_BYTELEN    16
index 44870917bd3c9060fc4e373be7ee7a3271234eef..e292d7a342d0c889f18bb8bd81bcbef225c040ae 100644 (file)
@@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \
        lib/module.c \
        lib/network.c \
        lib/nexthop.c \
-       lib/ns.c \
+       lib/netns_linux.c \
+       lib/netns_other.c \
        lib/openbsd-tree.c \
        lib/pid_output.c \
        lib/plist.c \
@@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \
        lib/wheel.c \
        lib/workqueue.c \
        lib/zclient.c \
+       lib/logicalrouter.c \
        # end
 
 lib/plist_clippy.c: $(CLIPPY_DEPS)
@@ -158,6 +160,7 @@ pkginclude_HEADERS += \
        lib/zassert.h \
        lib/zclient.h \
        lib/zebra.h \
+       lib/logicalrouter.h \
        # end
 
 nodist_pkginclude_HEADERS += \
index 2e15fa2f5c2c0c884b99cd6f2fdc0b68d3e1ab79..ea106b90a207570e5f48d8622f4a1e8ef26aef43 100644 (file)
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -21,6 +21,9 @@
 
 #include <zebra.h>
 
+/* for basename */
+#include <libgen.h>
+
 #include "if.h"
 #include "vrf.h"
 #include "vrf_int.h"
 #include "log.h"
 #include "memory.h"
 #include "command.h"
+#include "ns.h"
+
+/* default VRF ID value used when VRF backend is not NETNS */
+#define VRF_DEFAULT_INTERNAL 0
 
 DEFINE_MTYPE_STATIC(LIB, VRF, "VRF")
 DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map")
@@ -44,6 +51,8 @@ RB_GENERATE(vrf_name_head, vrf, name_entry, vrf_name_compare);
 struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id);
 struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
 
+static int vrf_backend;
+
 /*
  * Turn on/off debug code
  * for vrf.
@@ -61,7 +70,6 @@ struct vrf_master {
 };
 
 static int vrf_is_enabled(struct vrf *vrf);
-static void vrf_disable(struct vrf *vrf);
 
 /* VRF list existance check by name. */
 struct vrf *vrf_lookup_by_name(const char *name)
@@ -81,6 +89,54 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b)
        return strcmp(a->name, b->name);
 }
 
+/* if ns_id is different and not VRF_UNKNOWN,
+ * then update vrf identifier, and enable VRF
+ */
+static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr)
+{
+       ns_id_t vrf_id = (vrf_id_t)ns_id;
+       vrf_id_t old_vrf_id;
+       struct vrf *vrf = (struct vrf *)opaqueptr;
+
+       if (!vrf)
+               return;
+       old_vrf_id = vrf->vrf_id;
+       if (vrf_id == vrf->vrf_id)
+               return;
+       if (vrf->vrf_id != VRF_UNKNOWN)
+               RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
+       vrf->vrf_id = vrf_id;
+       RB_INSERT(vrf_id_head, &vrfs_by_id, vrf);
+       if (old_vrf_id == VRF_UNKNOWN)
+               vrf_enable((struct vrf *)vrf);
+}
+
+int vrf_switch_to_netns(vrf_id_t vrf_id)
+{
+       char *name;
+       struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+       /* VRF is default VRF. silently ignore */
+       if (!vrf || vrf->vrf_id == VRF_DEFAULT)
+               return 0;
+       /* VRF has no NETNS backend. silently ignore */
+       if (vrf->data.l.netns_name[0] == '\0')
+               return 0;
+       name = ns_netns_pathname(NULL, vrf->data.l.netns_name);
+       if (debug_vrf)
+               zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id);
+       return ns_switch_to_netns(name);
+}
+
+int vrf_switchback_to_initial(void)
+{
+       int ret = ns_switchback_to_initial();
+
+       if (ret == 0 && debug_vrf)
+               zlog_debug("VRF_SWITCHBACK");
+       return ret;
+}
+
 /* Get a VRF. If not found, create one.
  * Arg:
  *   name   - The name of the vrf.  May be NULL if unknown.
@@ -94,7 +150,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
        int new = 0;
 
        if (debug_vrf)
-               zlog_debug("VRF_GET: %s(%u)", name, vrf_id);
+               zlog_debug("VRF_GET: %s(%u)",
+                          name == NULL ? "(NULL)" : name, vrf_id);
 
        /* Nothing to see, move along here */
        if (!name && vrf_id == VRF_UNKNOWN)
@@ -109,8 +166,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
        if (vrf == NULL) {
                vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
                vrf->vrf_id = VRF_UNKNOWN;
-               RB_INIT(if_name_head, &vrf->ifaces_by_name);
-               RB_INIT(if_index_head, &vrf->ifaces_by_index);
                QOBJ_REG(vrf, vrf);
                new = 1;
 
@@ -134,7 +189,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
                strlcpy(vrf->name, name, sizeof(vrf->name));
                RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
        }
-
        if (new &&vrf_master.vrf_new_hook)
                (*vrf_master.vrf_new_hook)(vrf);
 
@@ -219,7 +273,7 @@ int vrf_enable(struct vrf *vrf)
  * The VRF_DELETE_HOOK callback will be called to inform
  * that they must release the resources in the VRF.
  */
-static void vrf_disable(struct vrf *vrf)
+void vrf_disable(struct vrf *vrf)
 {
        if (!vrf_is_enabled(vrf))
                return;
@@ -385,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
 {
        struct vrf *default_vrf;
 
+       /* initialise NS, in case VRF backend if NETNS */
+       ns_init();
        if (debug_vrf)
                zlog_debug("%s: Initializing VRF subsystem",
                           __PRETTY_FUNCTION__);
@@ -419,12 +475,17 @@ void vrf_terminate(void)
                zlog_debug("%s: Shutting down vrf subsystem",
                           __PRETTY_FUNCTION__);
 
-       while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) {
+       while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) {
+               vrf = RB_ROOT(vrf_id_head, &vrfs_by_id);
+
                /* Clear configured flag and invoke delete. */
                UNSET_FLAG(vrf->status, VRF_CONFIGURED);
                vrf_delete(vrf);
        }
-       while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) {
+
+       while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) {
+               vrf = RB_ROOT(vrf_name_head, &vrfs_by_name);
+
                /* Clear configured flag and invoke delete. */
                UNSET_FLAG(vrf->status, VRF_CONFIGURED);
                vrf_delete(vrf);
@@ -432,41 +493,163 @@ void vrf_terminate(void)
 }
 
 /* Create a socket for the VRF. */
-int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id)
+int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
+              char *interfacename)
 {
-       int ret = -1;
+       int ret, save_errno, ret2;
 
+       ret = vrf_switch_to_netns(vrf_id);
+       if (ret < 0)
+               zlog_err("%s: Can't switch to VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
        ret = socket(domain, type, protocol);
-
+       save_errno = errno;
+       ret2 = vrf_switchback_to_initial();
+       if (ret2 < 0)
+               zlog_err("%s: Can't switchback from VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
+       errno = save_errno;
+       if (ret <= 0)
+               return ret;
+       ret2 = vrf_bind(vrf_id, ret, interfacename);
+       if (ret2 < 0) {
+               close(ret);
+               ret = ret2;
+       }
        return ret;
 }
 
-/* vrf CLI commands */
-DEFUN_NOSH (vrf,
-       vrf_cmd,
-       "vrf NAME",
-       "Select a VRF to configure\n"
-       "VRF's name\n")
+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)
 {
-       int idx_name = 1;
-       const char *vrfname = argv[idx_name]->arg;
        struct vrf *vrfp;
 
        if (strlen(vrfname) > VRF_NAMSIZ) {
-               vty_out(vty,
-                       "%% VRF name %s is invalid: length exceeds "
-                       "%d characters\n",
-                       vrfname, VRF_NAMSIZ);
+               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);
 
-       VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+       if (vty)
+               VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+
+       if (vrf)
+               *vrf = vrfp;
+       return CMD_SUCCESS;
+}
+
+int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
+                            char *pathname, ns_id_t ns_id)
+{
+       struct ns *ns = NULL;
+
+       if (!vrf)
+               return CMD_WARNING_CONFIG_FAILED;
+       if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) {
+               if (vty)
+                       vty_out(vty,
+                               "VRF %u is already configured with VRF %s\n",
+                               vrf->vrf_id, vrf->name);
+               else
+                       zlog_warn("VRF %u is already configured with VRF %s\n",
+                                 vrf->vrf_id, vrf->name);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       if (vrf->ns_ctxt != NULL) {
+               ns = (struct ns *) vrf->ns_ctxt;
+               if (ns && 0 != strcmp(ns->name, pathname)) {
+                       if (vty)
+                               vty_out(vty,
+                                  "VRF %u already configured with NETNS %s\n",
+                                  vrf->vrf_id, ns->name);
+                       else
+                               zlog_warn(
+                                 "VRF %u already configured with NETNS %s",
+                                 vrf->vrf_id, ns->name);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       }
+       ns = ns_lookup_name(pathname);
+       if (ns && ns->vrf_ctxt) {
+               struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt;
+
+               if (vrf2 == vrf)
+                       return CMD_SUCCESS;
+               if (vty)
+                       vty_out(vty, "NS %s is already configured"
+                               " with VRF %u(%s)\n",
+                           ns->name, vrf2->vrf_id, vrf2->name);
+               else
+                       zlog_warn("NS %s is already configured with VRF %u(%s)",
+                                 ns->name, vrf2->vrf_id, vrf2->name);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       ns = ns_get_created(ns, pathname, ns_id);
+       ns->vrf_ctxt = (void *)vrf;
+       vrf->ns_ctxt = (void *)ns;
+       /* update VRF netns NAME */
+       if (vrf)
+               strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ);
+
+       if (!ns_enable(ns, vrf_update_vrf_id)) {
+               if (vty)
+                       vty_out(vty, "Can not associate NS %u with NETNS %s\n",
+                           ns->ns_id, ns->name);
+               else
+                       zlog_warn("Can not associate NS %u with NETNS %s",
+                                 ns->ns_id, ns->name);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
 
        return CMD_SUCCESS;
 }
 
+int vrf_is_mapped_on_netns(vrf_id_t vrf_id)
+{
+       struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+       if (!vrf || vrf->data.l.netns_name[0] == '\0')
+               return 0;
+       if (vrf->vrf_id == VRF_DEFAULT)
+               return 0;
+       return 1;
+}
+
+/* vrf CLI commands */
+DEFUN_NOSH (vrf,
+       vrf_cmd,
+       "vrf NAME",
+       "Select a VRF to configure\n"
+       "VRF's name\n")
+{
+       int idx_name = 1;
+       const char *vrfname = argv[idx_name]->arg;
+
+       return vrf_handler_create(vty, vrfname, NULL);
+}
+
 DEFUN_NOSH (no_vrf,
            no_vrf_cmd,
            "no vrf NAME",
@@ -500,6 +683,55 @@ DEFUN_NOSH (no_vrf,
 
 struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
 
+DEFUN_NOSH (vrf_netns,
+           vrf_netns_cmd,
+           "netns NAME",
+           "Attach VRF to a Namespace\n"
+           "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+       int idx_name = 1;
+       char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
+
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+       if (!pathname)
+               return CMD_WARNING_CONFIG_FAILED;
+       return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
+}
+
+DEFUN (no_vrf_netns,
+       no_vrf_netns_cmd,
+       "no netns [NAME]",
+       NO_STR
+       "Detach VRF from a Namespace\n"
+       "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+       struct ns *ns = NULL;
+
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+       if (!vrf_is_backend_netns()) {
+               vty_out(vty, "VRF backend is not Netns. Aborting\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       if (!vrf->ns_ctxt) {
+               vty_out(vty, "VRF %s(%u) is not configured with NetNS\n",
+                       vrf->name, vrf->vrf_id);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       ns = (struct ns *)vrf->ns_ctxt;
+
+       ns->vrf_ctxt = NULL;
+       vrf_disable(vrf);
+       /* vrf ID from VRF is necessary for Zebra
+        * so that propagate to other clients is done
+        */
+       ns_delete(ns);
+       vrf->ns_ctxt = NULL;
+       return CMD_SUCCESS;
+}
+
 /*
  * Debug CLI for vrf's
  */
@@ -552,4 +784,105 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty))
        install_element(CONFIG_NODE, &no_vrf_cmd);
        install_node(&vrf_node, writefunc);
        install_default(VRF_NODE);
+       if (vrf_is_backend_netns() && ns_have_netns()) {
+               /* Install NS commands. */
+               install_element(VRF_NODE, &vrf_netns_cmd);
+               install_element(VRF_NODE, &no_vrf_netns_cmd);
+       }
+}
+
+vrf_id_t vrf_get_default_id(void)
+{
+       struct vrf *vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME);
+
+       if (vrf)
+               return vrf->vrf_id;
+       if (vrf_is_backend_netns())
+               return ns_get_default_id();
+       else
+               return VRF_DEFAULT_INTERNAL;
+}
+
+int vrf_bind(vrf_id_t vrf_id, int fd, char *name)
+{
+       int ret = 0;
+
+       if (fd < 0 || name == NULL)
+               return fd;
+       if (vrf_is_mapped_on_netns(vrf_id))
+               return fd;
+#ifdef SO_BINDTODEVICE
+       ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name,
+                        strlen(name));
+       if (ret < 0)
+               zlog_debug("bind to interface %s failed, errno=%d",
+                          name, errno);
+#endif /* SO_BINDTODEVICE */
+       return ret;
+}
+int vrf_getaddrinfo(const char *node, const char *service,
+                   const struct addrinfo *hints,
+                   struct addrinfo **res, vrf_id_t vrf_id)
+{
+       int ret, ret2, save_errno;
+
+       ret = vrf_switch_to_netns(vrf_id);
+       if (ret < 0)
+               zlog_err("%s: Can't switch to VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
+       ret = getaddrinfo(node, service, hints, res);
+       save_errno = errno;
+       ret2 = vrf_switchback_to_initial();
+       if (ret2 < 0)
+               zlog_err("%s: Can't switchback from VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
+       errno = save_errno;
+       return ret;
+}
+
+int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params)
+{
+       int ret, saved_errno, rc;
+
+       ret = vrf_switch_to_netns(vrf_id);
+       if (ret < 0) {
+               zlog_err("%s: Can't switch to VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
+               return 0;
+       }
+       rc = ioctl(d, request, params);
+       saved_errno = errno;
+       ret = vrf_switchback_to_initial();
+       if (ret < 0)
+               zlog_err("%s: Can't switchback from VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
+       errno = saved_errno;
+       return rc;
+}
+
+int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
+                        char *interfacename)
+{
+       int ret, save_errno, ret2;
+
+       ret = vrf_switch_to_netns(vrf_id);
+       if (ret < 0)
+               zlog_err("%s: Can't switch to VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
+       ret = sockunion_socket(su);
+       save_errno = errno;
+       ret2 = vrf_switchback_to_initial();
+       if (ret2 < 0)
+               zlog_err("%s: Can't switchback from VRF %u (%s)",
+                        __func__, vrf_id, safe_strerror(errno));
+       errno = save_errno;
+
+       if (ret <= 0)
+               return ret;
+       ret2 = vrf_bind(vrf_id, ret, interfacename);
+       if (ret2 < 0) {
+               close(ret);
+               ret = ret2;
+       }
+       return ret;
 }
index 99c048c7025361987d7ab700fb05d0a2dff2b740..062e6f3d8df1c656f5a4949c73f4369110692e22 100644 (file)
--- a/lib/vrf.h
+++ b/lib/vrf.h
 #include "linklist.h"
 #include "qobj.h"
 #include "vty.h"
-
-/* The default NS ID */
-#define NS_DEFAULT 0
+#include "ns.h"
 
 /* The default VRF ID */
-#define VRF_DEFAULT 0
 #define VRF_UNKNOWN UINT32_MAX
 
 /* Pending: May need to refine this. */
@@ -42,6 +39,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX };
 #endif
 
 #define VRF_NAMSIZ      36
+#define NS_NAMSIZ       16
 
 #define VRF_DEFAULT_NAME    "Default-IP-Routing-Table"
 
@@ -60,6 +58,7 @@ struct vrf_data {
        union {
                struct {
                        uint32_t table_id;
+                       char netns_name[NS_NAMSIZ];
                } l;
        };
 };
@@ -88,6 +87,9 @@ struct vrf {
        /* The table_id from the kernel */
        struct vrf_data data;
 
+       /* Back pointer to namespace context */
+       void *ns_ctxt;
+
        QOBJ_FIELDS
 };
 RB_HEAD(vrf_id_head, vrf);
@@ -96,6 +98,9 @@ RB_HEAD(vrf_name_head, vrf);
 RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
 DECLARE_QOBJ_TYPE(vrf)
 
+/* Allow VRF with netns as backend */
+#define VRF_BACKEND_VRF_LITE 0
+#define VRF_BACKEND_NETNS    1
 
 extern struct vrf_id_head vrfs_by_id;
 extern struct vrf_name_head vrfs_by_name;
@@ -195,17 +200,85 @@ extern void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
  */
 extern void vrf_terminate(void);
 
-extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
-
 /*
- * VRF utilities
+ * Utilities to create networks objects,
+ * or call network operations
  */
 
 /* Create a socket serving for the given VRF */
-extern int vrf_socket(int, int, int, vrf_id_t);
+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 Debugging
+ * VRF backend routines
+ * should be called from zebra only
+ */
+
+/* VRF vty command initialisation
+ */
+extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
+
+/* VRF vty debugging
  */
 extern void vrf_install_commands(void);
+
+/*
+ * VRF utilities
+ */
+
+/* API for configuring VRF backend
+ * should be called from zebra only
+ */
+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*/
index 714888a3f3f260625fe4621c5de3828e38cd2e64..ad91eb504bafe483fef1d3e52781ddff637ff813 100644 (file)
@@ -1210,14 +1210,20 @@ stream_failure:
 }
 
 bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
+                             uint32_t *tableid,
                              enum zapi_route_notify_owner *note)
 {
+       uint32_t t;
+
        STREAM_GET(note, s, sizeof(*note));
 
        STREAM_GETC(s, p->family);
        STREAM_GETC(s, p->prefixlen);
        STREAM_GET(&p->u.prefix, s,
-                  PSIZE(p->prefixlen));
+                  prefix_blen(p));
+       STREAM_GETL(s, t);
+
+       *tableid = t;
 
        return true;
 
@@ -1402,8 +1408,8 @@ static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
 
        /* Lookup/create vrf by vrf_id. */
        vrf = vrf_get(vrf_id, vrfname_tmp);
-       vrf->data = data;
-
+       vrf->data.l.table_id = data.l.table_id;
+       memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ);
        vrf_enable(vrf);
 }
 
@@ -2363,9 +2369,9 @@ static int zclient_read(struct thread *thread)
                                                     vrf_id);
                break;
        case ZEBRA_ROUTE_NOTIFY_OWNER:
-               if (zclient->notify_owner)
-                       (*zclient->notify_owner)(command, zclient,
-                                                length, vrf_id);
+               if (zclient->route_notify_owner)
+                       (*zclient->route_notify_owner)(command, zclient, length,
+                                                      vrf_id);
                break;
        default:
                break;
index d8a70c6cf339a263774152eee3f5dfc59b946c0d..4c84af1f61b09878baa084f5a4a131038207700f 100644 (file)
@@ -213,8 +213,8 @@ struct zclient {
        int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
        int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
        int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);
-       int (*notify_owner)(int command, struct zclient *zclient,
-                           uint16_t length, vrf_id_t vrf_id);
+       int (*route_notify_owner)(int command, struct zclient *zclient,
+                                 uint16_t length, vrf_id_t vrf_id);
 };
 
 /* Zebra API message flag. */
@@ -513,6 +513,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command,
 extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *);
 extern int zapi_route_decode(struct stream *, struct zapi_route *);
 bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
+                             uint32_t *tableid,
                              enum zapi_route_notify_owner *note);
 extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
 extern bool zapi_nexthop_update_decode(struct stream *s,
index 022a5a138af117483c9665d1c7948ebd493f16a3..045634d8ab631d11034600a39536c73dd3aa54dd 100644 (file)
@@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
        return ret;
 }
 
-int ospf_bind_vrfdevice(struct ospf *ospf, int ospf_sock)
-{
-       int ret = 0;
-
-#ifdef SO_BINDTODEVICE
-
-       if (ospf && ospf->vrf_id != VRF_DEFAULT &&
-           ospf->vrf_id != VRF_UNKNOWN) {
-               ret = setsockopt(ospf_sock, SOL_SOCKET, SO_BINDTODEVICE,
-                                ospf->name,
-                                strlen(ospf->name));
-               if (ret < 0) {
-                       int save_errno = errno;
-
-                       zlog_warn("%s: Could not setsockopt SO_BINDTODEVICE %s",
-                                       __PRETTY_FUNCTION__,
-                                       safe_strerror(save_errno));
-               }
-
-       }
-#endif
-       return ret;
-}
-
 int ospf_sock_init(struct ospf *ospf)
 {
        int ospf_sock;
        int ret, hincl = 1;
        int bufsize = (8 * 1024 * 1024);
 
+       /* silently ignore. already done */
+       if (ospf->fd > 0)
+               return -1;
+
+       if (ospf->vrf_id == VRF_UNKNOWN) {
+               /* silently return since VRF is not ready */
+               return -1;
+       }
        if (ospfd_privs.change(ZPRIVS_RAISE)) {
                zlog_err("ospf_sock_init: could not raise privs, %s",
                         safe_strerror(errno));
        }
 
-       ospf_sock = socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP);
+       ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP,
+                              ospf->vrf_id, ospf->name);
        if (ospf_sock < 0) {
                int save_errno = errno;
 
@@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf)
                exit(1);
        }
 
-       ret = ospf_bind_vrfdevice(ospf, ospf_sock);
-       if (ret < 0) {
-               close(ospf_sock);
-               goto out;
-       }
-
 #ifdef IP_HDRINCL
        /* we will include IP header with packet */
        ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl,
index 41a7abda7013ed1af4b6e1d25d7d859c0576b440..cbaf1323277cb18cfcf9036f0e31e5309efe14db 100644 (file)
@@ -30,6 +30,5 @@ extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t);
 extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t);
 extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t);
 extern int ospf_sock_init(struct ospf *ospf);
-extern int ospf_bind_vrfdevice(struct ospf *, int);
 
 #endif /* _ZEBRA_OSPF_NETWORK_H */
index 1c586d252cb752ff95880f97c28e1cc5df1a9a0e..009fd997eab9a34beea663b7fb7a879474d92f87 100644 (file)
@@ -75,6 +75,7 @@ static void ospf_opaque_funclist_init(void);
 static void ospf_opaque_funclist_term(void);
 static void free_opaque_info_per_type(void *val);
 static void free_opaque_info_per_id(void *val);
+static void free_opaque_info_owner(void *val);
 static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
 static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
 
@@ -439,9 +440,11 @@ void ospf_delete_opaque_functab(u_char lsa_type, u_char opaque_type)
                        if (functab->opaque_type == opaque_type) {
                                /* Cleanup internal control information, if it
                                 * still remains. */
-                               if (functab->oipt != NULL)
+                               if (functab->oipt != NULL) {
                                        free_opaque_info_per_type(
                                                functab->oipt);
+                                       free_opaque_info_owner(functab->oipt);
+                               }
 
                                /* Dequeue listnode entry from the list. */
                                listnode_delete(funclist, functab);
@@ -572,6 +575,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
                top = ospf_lookup_by_vrf_id(new->vrf_id);
                if (new->area != NULL && (top = new->area->ospf) == NULL) {
                        free_opaque_info_per_type((void *)oipt);
+                       free_opaque_info_owner(oipt);
                        oipt = NULL;
                        goto out; /* This case may not exist. */
                }
@@ -583,6 +587,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
                        "register_opaque_info_per_type: Unexpected LSA-type(%u)",
                        new->data->type);
                free_opaque_info_per_type((void *)oipt);
+               free_opaque_info_owner(oipt);
                oipt = NULL;
                goto out; /* This case may not exist. */
        }
@@ -600,6 +605,35 @@ out:
        return oipt;
 }
 
+/* Remove "oipt" from its owner's self-originated LSA list. */
+static void free_opaque_info_owner(void *val)
+{
+       struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
+
+        switch (oipt->lsa_type) {
+        case OSPF_OPAQUE_LINK_LSA: {
+                struct ospf_interface *oi =
+                        (struct ospf_interface *)(oipt->owner);
+                listnode_delete(oi->opaque_lsa_self, oipt);
+                break;
+        }
+        case OSPF_OPAQUE_AREA_LSA: {
+                struct ospf_area *area = (struct ospf_area *)(oipt->owner);
+                listnode_delete(area->opaque_lsa_self, oipt);
+                break;
+        }
+        case OSPF_OPAQUE_AS_LSA: {
+                struct ospf *top = (struct ospf *)(oipt->owner);
+                listnode_delete(top->opaque_lsa_self, oipt);
+                break;
+        }
+        default:
+                zlog_warn("free_opaque_info_owner: Unexpected LSA-type(%u)",
+                          oipt->lsa_type);
+                break; /* This case may not exist. */
+        }
+}
+
 static void free_opaque_info_per_type(void *val)
 {
        struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
index 86703596102111d31a6a6c58ed874db8f5a22069..881226683c572f1a183f6273cd301be8e9fa89c9 100644 (file)
@@ -3897,6 +3897,10 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi,
                zlog_debug("listcount = %d, [%s]dst %s", listcount(update),
                           IF_NAME(oi), inet_ntoa(addr));
 
+       /* Check that we have really something to process */
+       if (listcount(update) == 0)
+               return;
+
        op = ospf_ls_upd_packet_new(update, oi);
 
        /* Prepare OSPF common header. */
index b7a47602d0b16037a5090ab49a8054235cf75f87..f2769c6f38e108ccf6a24e4c336c6334cff05804 100644 (file)
@@ -337,6 +337,7 @@ static struct route_map_rule_cmd route_match_tag_cmd = {
 };
 
 struct ospf_metric {
+       enum { metric_increment, metric_decrement, metric_absolute } type;
        bool used;
        u_int32_t metric;
 };
@@ -356,8 +357,19 @@ static route_map_result_t route_set_metric(void *rule, struct prefix *prefix,
                ei = object;
 
                /* Set metric out value. */
-               if (metric->used)
+               if (!metric->used)
+                       return RMAP_OKAY;
+               if (metric->type == metric_increment)
+                       ei->route_map_set.metric += metric->metric;
+               if (metric->type == metric_decrement)
+                       ei->route_map_set.metric -= metric->metric;
+               if (metric->type == metric_absolute)
                        ei->route_map_set.metric = metric->metric;
+
+               if ((signed int)ei->route_map_set.metric < 1)
+                       ei->route_map_set.metric = -1;
+               if (ei->route_map_set.metric > OSPF_LS_INFINITY)
+                       ei->route_map_set.metric = OSPF_LS_INFINITY;
        }
        return RMAP_OKAY;
 }
@@ -370,23 +382,28 @@ static void *route_set_metric_compile(const char *arg)
        metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_int32_t));
        metric->used = false;
 
-       /* OSPF doesn't support the +/- in
-          set metric <+/-metric> check
-          Ignore the +/- component */
-       if (!all_digit(arg)) {
-               if ((arg[0] == '+' || arg[0] == '-') && all_digit(arg + 1)) {
-                       zlog_warn("OSPF does not support 'set metric +/-'");
-                       arg++;
-               } else {
-                       if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt"))
-                               zlog_warn(
-                                       "OSPF does not support 'set metric +rtt / -rtt'");
-
-                       return metric;
-               }
+       if (all_digit(arg))
+               metric->type = metric_absolute;
+
+       if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) {
+               zlog_warn("OSPF does not support 'set metric +rtt / -rtt'");
+               return metric;
+       }
+
+       if ((arg[0] == '+') && all_digit(arg + 1)) {
+               metric->type = metric_increment;
+               arg++;
        }
+
+       if ((arg[0] == '-') && all_digit(arg + 1)) {
+               metric->type = metric_decrement;
+               arg++;
+       }
+
        metric->metric = strtoul(arg, NULL, 10);
-       metric->used = true;
+
+       if (metric->metric)
+               metric->used = true;
 
        return metric;
 }
index 09350b45a857713d77a5199213e62ea8ebc915e7..68fb2f5676dc4eb4346be1e5016f477145420f1f 100644 (file)
@@ -3693,7 +3693,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
 {
        struct interface *ifp;
        struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
-       json_object *json_vrf = NULL, *json_intf_array = NULL;
+       json_object *json_vrf = NULL;
        json_object *json_interface_sub = NULL, *json_interface = NULL;
 
        if (use_json) {
@@ -3701,7 +3701,7 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
                        json_vrf = json_object_new_object();
                else
                        json_vrf = json;
-               json_intf_array = json_object_new_array();
+               json_interface = json_object_new_object();
        }
 
        if (ospf->instance) {
@@ -3715,15 +3715,10 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
        ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
 
        if (intf_name == NULL) {
-               if (use_json)
-                       json_object_object_add(json_vrf, "interfaces",
-                                      json_intf_array);
                /* Show All Interfaces.*/
                FOR_ALL_INTERFACES (vrf, ifp) {
                        if (ospf_oi_count(ifp)) {
                                if (use_json) {
-                                       json_interface =
-                                               json_object_new_object();
                                        json_interface_sub =
                                                json_object_new_object();
                                }
@@ -3732,14 +3727,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
                                                           use_json);
 
                                if (use_json) {
-                                       json_object_array_add(json_intf_array,
-                                                             json_interface);
                                        json_object_object_add(
                                                json_interface, ifp->name,
                                                json_interface_sub);
                                }
                        }
                }
+               if (use_json)
+                       json_object_object_add(json_vrf, "interfaces",
+                                      json_interface);
        } else {
                /* Interface name is specified. */
                ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
@@ -3753,19 +3749,17 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
                        if (use_json) {
                                json_interface_sub = json_object_new_object();
                                json_interface = json_object_new_object();
-                               json_object_object_add(json_vrf, "interfaces",
-                                                      json_intf_array);
                        }
 
                        show_ip_ospf_interface_sub(
                                vty, ospf, ifp, json_interface_sub, use_json);
 
                        if (use_json) {
-                               json_object_array_add(json_intf_array,
-                                                     json_interface);
                                json_object_object_add(json_interface,
                                                       ifp->name,
                                                       json_interface_sub);
+                               json_object_object_add(json_vrf, "interfaces",
+                                                      json_interface);
                        }
                }
        }
@@ -4300,7 +4294,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
 {
        struct ospf_interface *oi;
        struct listnode *node;
-       json_object *json_vrf = NULL, *json_nbr_array = NULL;
+       json_object *json_vrf = NULL;
        json_object *json_nbr_sub = NULL;
 
        if (use_json) {
@@ -4308,7 +4302,7 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
                        json_vrf = json_object_new_object();
                else
                        json_vrf = json;
-               json_nbr_array = json_object_new_array();
+               json_nbr_sub = json_object_new_object();
        }
 
        if (ospf->instance) {
@@ -4322,21 +4316,16 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
        ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
        if (!use_json)
                show_ip_ospf_neighbour_header(vty);
-       else
-               json_object_object_add(json_vrf, "neighbors",
-                                      json_nbr_array);
 
        for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
                if (ospf_interface_neighbor_count(oi) == 0)
                        continue;
-               if (use_json) {
-                       json_nbr_sub = json_object_new_object();
-                       json_object_array_add(json_nbr_array, json_nbr_sub);
-               }
                show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json);
        }
 
        if (use_json) {
+               json_object_object_add(json_vrf, "neighbors",
+                                      json_nbr_sub);
                if (use_vrf) {
                        if (ospf->vrf_id == VRF_DEFAULT)
                                json_object_object_add(json, "default",
index 86a3293d7110bc4987e5ba0fc3438814eeaf2924..79af4a55fb9e1b558f7c656141a35f35c884a1c4 100644 (file)
@@ -308,12 +308,6 @@ static struct ospf *ospf_new(u_short instance, const char *name)
                         new->lsa_refresh_interval, &new->t_lsa_refresher);
        new->lsa_refresher_started = monotime(NULL);
 
-       if ((ospf_sock_init(new)) < 0) {
-               zlog_err(
-                       "ospf_new: fatal error: ospf_sock_init was unable to open "
-                       "a socket");
-               exit(1);
-       }
        if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) {
                zlog_err(
                        "ospf_new: fatal error: stream_new(%u) failed allocating ibuf",
@@ -321,7 +315,6 @@ static struct ospf *ospf_new(u_short instance, const char *name)
                exit(1);
        }
        new->t_read = NULL;
-       thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
        new->oi_write_q = list_new();
        new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT;
 
@@ -332,6 +325,16 @@ static struct ospf *ospf_new(u_short instance, const char *name)
 
        QOBJ_REG(new, ospf);
 
+       new->fd = -1;
+       if ((ospf_sock_init(new)) < 0) {
+               if (new->vrf_id != VRF_UNKNOWN)
+                       zlog_warn(
+                                 "%s: ospf_sock_init is unable to open a socket",
+                                 __func__);
+               return new;
+       }
+       thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
+
        return new;
 }
 
@@ -2049,7 +2052,8 @@ static int ospf_vrf_delete(struct vrf *vrf)
 static int ospf_vrf_enable(struct vrf *vrf)
 {
        struct ospf *ospf = NULL;
-       vrf_id_t old_vrf_id = VRF_DEFAULT;
+       vrf_id_t old_vrf_id;
+       int ret = 0;
 
        if (IS_DEBUG_OSPF_EVENT)
                zlog_debug("%s: VRF %s id %u enabled",
@@ -2070,13 +2074,15 @@ static int ospf_vrf_enable(struct vrf *vrf)
                                zlog_err("ospf_sock_init: could not raise privs, %s",
                                         safe_strerror(errno));
                        }
-                       if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0)
-                               return 0;
+                       ret = ospf_sock_init(ospf);
                        if (ospfd_privs.change(ZPRIVS_LOWER)) {
                                zlog_err("ospf_sock_init: could not lower privs, %s",
                                         safe_strerror(errno));
                        }
-
+                       if (ret < 0 || ospf->fd <= 0)
+                               return 0;
+                       thread_add_read(master, ospf_read, ospf,
+                                       ospf->fd, &ospf->t_read);
                        ospf->oi_running = 1;
                        ospf_zebra_vrf_register(ospf);
                        ospf_router_id_update(ospf);
@@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf)
                if (IS_DEBUG_OSPF_EVENT)
                        zlog_debug("%s: ospf old_vrf_id %d unlinked",
                                    __PRETTY_FUNCTION__, old_vrf_id);
+               thread_cancel(ospf->t_read);
+               close(ospf->fd);
+               ospf->fd = -1;
        }
 
        /* Note: This is a callback, the VRF will be deleted by the caller. */
index e23216b058a7de476952e7b25f8219b74fdb61a3..1f56cfaecdabd2dac7344f859ef8710ce6b03079 100644 (file)
@@ -2,6 +2,7 @@
 Makefile.in
 libpim.a
 pimd
+mtracebis
 test_igmpv3_join
 tags
 TAGS
index 43dd823ae58cd184d00cab3adf01294e6c12e821..120708ba14df8938196b59a1fdb7a00a67137489 100644 (file)
@@ -173,4 +173,10 @@ C19 Provision to prevent group mode clash
     flow. There could be some provision to prevent such a behavior,
     but currently there is none.
 
+C20 Multicast traceroute module is based on:
+    draft-ietf-idmr-traceroute-ipm-07
+    It only implements, so far, weak traceroutes. The multicast routing
+    state of the router is not quieried but RPF path is followed along
+    PIM and IGMP enabled interfaces.
+
 -x-
index c545eca56e45d034132b5dac63c3496416c7c82b..6f2e020bd87f5c146d5c5574fa55790f7671cbcd 100644 (file)
@@ -59,6 +59,7 @@ debug commands:
        clear ip pim interfaces         Reset PIM interfaces
        clear ip pim oil                        Rescan PIM OIL (output interface list)
        debug igmp                      IGMP protocol activity
+       debug mtrace                    Mtrace protocol activity
        debug mroute                    PIM interaction with kernel MFC cache
        debug pim                       PIM protocol activity
        debug pim zebra                 ZEBRA protocol activity
@@ -76,4 +77,6 @@ statistics commands:
        pimd:
        show memory pim         PIM memory statistics
 
+vtysh:
+       mtrace                  Multicast traceroute
 -x-
diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c
new file mode 100644 (file)
index 0000000..1e7aee8
--- /dev/null
@@ -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__ */
diff --git a/pimd/mtracebis_netlink.c b/pimd/mtracebis_netlink.c
new file mode 100644 (file)
index 0000000..42b80b2
--- /dev/null
@@ -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__ */
diff --git a/pimd/mtracebis_netlink.h b/pimd/mtracebis_netlink.h
new file mode 100644 (file)
index 0000000..7a60ead
--- /dev/null
@@ -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__ */
diff --git a/pimd/mtracebis_routeget.c b/pimd/mtracebis_routeget.c
new file mode 100644 (file)
index 0000000..d75aaa3
--- /dev/null
@@ -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__ */
diff --git a/pimd/mtracebis_routeget.h b/pimd/mtracebis_routeget.h
new file mode 100644 (file)
index 0000000..18ecf6e
--- /dev/null
@@ -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__ */
index 11aeeddf93ae96e96eb72d6883f75ffad51d3e31..fc07b706a9797ce9560f8f95325f11ce484f2044 100644 (file)
@@ -7311,6 +7311,27 @@ DEFUN (no_debug_msdp_packets,
 ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
       UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
 
+DEFUN (debug_mtrace,
+       debug_mtrace_cmd,
+       "debug mtrace",
+       DEBUG_STR
+       DEBUG_MTRACE_STR)
+{
+       PIM_DO_DEBUG_MTRACE;
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_mtrace,
+       no_debug_mtrace_cmd,
+       "no debug mtrace",
+       NO_STR
+       DEBUG_STR
+       DEBUG_MTRACE_STR)
+{
+       PIM_DONT_DEBUG_MTRACE;
+       return CMD_SUCCESS;
+}
+
 DEFUN_NOSH (show_debugging_pim,
            show_debugging_pim_cmd,
            "show debugging [pim]",
@@ -8721,6 +8742,8 @@ void pim_cmd_init(void)
        install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
        install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
        install_element(ENABLE_NODE, &undebug_msdp_packets_cmd);
+       install_element(ENABLE_NODE, &debug_mtrace_cmd);
+       install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
 
        install_element(CONFIG_NODE, &debug_igmp_cmd);
        install_element(CONFIG_NODE, &no_debug_igmp_cmd);
@@ -8763,6 +8786,8 @@ void pim_cmd_init(void)
        install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
        install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
        install_element(CONFIG_NODE, &undebug_msdp_packets_cmd);
+       install_element(CONFIG_NODE, &debug_mtrace_cmd);
+       install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
 
        install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
        install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);
index 8867514876a8283a6d3622a8a40192d473e7a563..b58da30bdd64847fb9d873d783d34c8e5db86da0 100644 (file)
@@ -63,6 +63,7 @@
 #define DEBUG_MSDP_EVENTS_STR                       "MSDP protocol events\n"
 #define DEBUG_MSDP_INTERNAL_STR                     "MSDP protocol internal\n"
 #define DEBUG_MSDP_PACKETS_STR                      "MSDP protocol packets\n"
+#define DEBUG_MTRACE_STR                            "Mtrace protocol activity\n"
 
 void pim_cmd_init(void);
 
index f02cf7ed31c4555688646f64cb570e68268841cf..a807c69c600db53667835def78e2ab28f801cbf0 100644 (file)
@@ -85,9 +85,11 @@ static void *if_list_clean(struct pim_interface *pim_ifp)
        if (pim_ifp->sec_addr_list)
                list_delete_and_null(&pim_ifp->sec_addr_list);
 
-       while ((ch = RB_ROOT(pim_ifchannel_rb,
-                            &pim_ifp->ifchannel_rb)) != NULL)
+       while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
+               ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
+
                pim_ifchannel_delete(ch);
+       }
 
        XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
 
@@ -250,9 +252,11 @@ void pim_if_delete(struct interface *ifp)
        if (pim_ifp->boundary_oil_plist)
                XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
 
-       while ((ch = RB_ROOT(pim_ifchannel_rb,
-                            &pim_ifp->ifchannel_rb)) != NULL)
+       while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
+               ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
+
                pim_ifchannel_delete(ch);
+       }
 
        XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
 
index 7d3b783adf63569ce3e6119edfa7dbfece5a7c38..4d564e504693720c16e54a4be626d4f5e9115796 100644 (file)
@@ -211,8 +211,9 @@ void pim_ifchannel_delete_all(struct interface *ifp)
        if (!pim_ifp)
                return;
 
-       while ((ch = RB_ROOT(pim_ifchannel_rb,
-                            &pim_ifp->ifchannel_rb)) != NULL) {
+       while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
+               ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
+
                pim_ifchannel_delete(ch);
        }
 }
index 7524119e52ad92931e96ebf7cf23d22d9e65e16a..05224203649baeba21900ea0432e2e92f7025e9e 100644 (file)
@@ -29,6 +29,7 @@
 #include "pim_igmp.h"
 #include "pim_igmpv2.h"
 #include "pim_igmpv3.h"
+#include "pim_igmp_mtrace.h"
 #include "pim_iface.h"
 #include "pim_sock.h"
 #include "pim_mroute.h"
@@ -504,6 +505,16 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
        case PIM_IGMP_V2_LEAVE_GROUP:
                return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
                                          igmp_msg, igmp_msg_len);
+
+       case PIM_IGMP_MTRACE_RESPONSE:
+               return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
+                                                from_str, igmp_msg,
+                                                igmp_msg_len);
+               break;
+       case PIM_IGMP_MTRACE_QUERY_REQUEST:
+               return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
+                                               from_str, igmp_msg,
+                                               igmp_msg_len);
        }
 
        zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
index 275f25f63f21817a3a3b910c7ae9f954fa672456..962c50e76a66eb0f2d75d84d5c6f36d001d45762 100644 (file)
@@ -37,6 +37,8 @@
 #define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
 #define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
 #define PIM_IGMP_V2_LEAVE_GROUP       (0x17)
+#define PIM_IGMP_MTRACE_RESPONSE      (0x1E)
+#define PIM_IGMP_MTRACE_QUERY_REQUEST (0x1F)
 #define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
 
 #define IGMP_V3_REPORT_HEADER_SIZE    (8)
diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c
new file mode 100644 (file)
index 0000000..feb326c
--- /dev/null
@@ -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);
+}
diff --git a/pimd/pim_igmp_mtrace.h b/pimd/pim_igmp_mtrace.h
new file mode 100644 (file)
index 0000000..b5c1008
--- /dev/null
@@ -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 */
index c45b0ce14c34ca46e327f62811870db55b8bb864..53bbf54f3e3f36897645db0bd7d77682fdff5493 100644 (file)
@@ -135,8 +135,8 @@ void pim_channel_oil_free(struct channel_oil *c_oil)
        XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
 }
 
-static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
-                                               struct prefix_sg *sg)
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+                                        struct prefix_sg *sg)
 {
        struct channel_oil *c_oil = NULL;
        struct channel_oil lookup;
index 1168ba0a8fe9b8a5c3520a4dc00649e3c4027501..94d3840e98db72aafa561e706620cac66f0ed399 100644 (file)
@@ -86,6 +86,8 @@ void pim_oil_init(struct pim_instance *pim);
 void pim_oil_terminate(struct pim_instance *pim);
 
 void pim_channel_oil_free(struct channel_oil *c_oil);
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+                                        struct prefix_sg *sg);
 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
                                        struct prefix_sg *sg,
                                        int input_vif_index);
index 791680a9114237c1b16a264393b4a892453d52ed..688bc42c3dddf3c8682bf4e7b6c8920bba00278b 100644 (file)
@@ -78,6 +78,11 @@ int pim_debug_config_write(struct vty *vty)
                ++writes;
        }
 
+       if (PIM_DEBUG_MTRACE) {
+               vty_out(vty, "debug mtrace\n");
+               ++writes;
+       }
+
        if (PIM_DEBUG_MROUTE_DETAIL) {
                vty_out(vty, "debug mroute detail\n");
                ++writes;
index cd00a2df46b96a61f9a2babd4eb5cdab44340fb0..de7f2593196ce07ec621aa127376c7939412e2d0 100644 (file)
 #define PIM_MASK_PIM_NHT             (1 << 22)
 #define PIM_MASK_PIM_NHT_DETAIL      (1 << 23)
 #define PIM_MASK_PIM_NHT_RP          (1 << 24)
+#define PIM_MASK_MTRACE              (1 << 25)
 /* Remember 32 bits!!! */
 
 /* PIM error codes */
@@ -188,6 +189,7 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DEBUG_PIM_NHT             (qpim_debugs & PIM_MASK_PIM_NHT)
 #define PIM_DEBUG_PIM_NHT_DETAIL      (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL)
 #define PIM_DEBUG_PIM_NHT_RP          (qpim_debugs & PIM_MASK_PIM_NHT_RP)
+#define PIM_DEBUG_MTRACE              (qpim_debugs & PIM_MASK_MTRACE)
 
 #define PIM_DEBUG_EVENTS       (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS))
 #define PIM_DEBUG_PACKETS      (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS))
@@ -216,6 +218,7 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DO_DEBUG_MSDP_INTERNAL       (qpim_debugs |= PIM_MASK_MSDP_INTERNAL)
 #define PIM_DO_DEBUG_PIM_NHT             (qpim_debugs |= PIM_MASK_PIM_NHT)
 #define PIM_DO_DEBUG_PIM_NHT_RP          (qpim_debugs |= PIM_MASK_PIM_NHT_RP)
+#define PIM_DO_DEBUG_MTRACE              (qpim_debugs |= PIM_MASK_MTRACE)
 
 #define PIM_DONT_DEBUG_PIM_EVENTS          (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
 #define PIM_DONT_DEBUG_PIM_PACKETS         (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
@@ -240,6 +243,7 @@ extern int32_t qpim_register_probe_time;
 #define PIM_DONT_DEBUG_MSDP_INTERNAL       (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL)
 #define PIM_DONT_DEBUG_PIM_NHT             (qpim_debugs &= ~PIM_MASK_PIM_NHT)
 #define PIM_DONT_DEBUG_PIM_NHT_RP          (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP)
+#define PIM_DONT_DEBUG_MTRACE              (qpim_debugs &= ~PIM_MASK_MTRACE)
 
 void pim_init(void);
 void pim_terminate(void);
index 2fcb06157656d6bff33da1efe22c8e966a83924e..2254362221bfa9bd98a1d7499f3775d28f5ba1fa 100644 (file)
@@ -5,6 +5,7 @@
 if PIMD
 noinst_LIBRARIES += pimd/libpim.a
 sbin_PROGRAMS += pimd/pimd
+bin_PROGRAMS += pimd/mtracebis
 noinst_PROGRAMS += pimd/test_igmpv3_join
 dist_examples_DATA += pimd/pimd.conf.sample
 endif
@@ -18,6 +19,7 @@ pimd_libpim_a_SOURCES = \
        pimd/pim_iface.c \
        pimd/pim_ifchannel.c \
        pimd/pim_igmp.c \
+       pimd/pim_igmp_mtrace.c \
        pimd/pim_igmpv2.c \
        pimd/pim_igmpv3.c \
        pimd/pim_instance.c \
@@ -66,6 +68,7 @@ noinst_HEADERS += \
        pimd/pim_ifchannel.h \
        pimd/pim_igmp.h \
        pimd/pim_igmp_join.h \
+       pimd/pim_igmp_mtrace.h \
        pimd/pim_igmpv2.h \
        pimd/pim_igmpv3.h \
        pimd/pim_instance.h \
@@ -101,6 +104,8 @@ noinst_HEADERS += \
        pimd/pim_zebra.h \
        pimd/pim_zlookup.h \
        pimd/pimd.h \
+       pimd/mtracebis_netlink.h \
+       pimd/mtracebis_routeget.h \
        # end
 
 pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@
@@ -108,3 +113,9 @@ pimd_pimd_SOURCES = pimd/pim_main.c
 
 pimd_test_igmpv3_join_LDADD = lib/libfrr.la
 pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
+
+pimd_mtracebis_LDADD =  lib/libfrr.la
+pimd_mtracebis_SOURCES = pimd/mtracebis.c \
+                        pimd/mtracebis_netlink.c \
+                        pimd/mtracebis_routeget.c \
+                        # end
diff --git a/render_md.py b/render_md.py
deleted file mode 100644 (file)
index 16c4bbe..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-# written 2016 by David Lamparter, placed in Public Domain.
-import sys, markdown
-
-template = '''<html><head><meta charset="UTF-8"><style type="text/css">
-body { max-width: 45em; margin: auto; margin-top: 2em; margin-bottom: 2em;
-    font-family:Fira Sans,sans-serif; text-align: justify;
-    counter-reset: ch2; }
-pre, code { font-family:Fira Mono,monospace; }
-pre > code { display: block; padding:0.5em; border:1px solid black;
-    background-color:#eee; color:#000; }
-h2:before { content: counter(ch2) ". "; counter-increment: ch2; }
-h2 { clear: both; margin-top: 3em; text-decoration: underline; counter-reset: ch3; }
-h3:before { content: counter(ch2) "." counter(ch3) ". "; counter-increment: ch3; }
-h3 { clear: both; margin-top: 2em; font-weight: normal; font-style: italic; }
-h4 { font-weight: normal; font-style: italic; }
-img[alt~="float-right"] { float:right; margin-left:2em; margin-bottom:2em; }
-</style></head><body>
-%s
-</body></html>
-'''
-
-md = markdown.Markdown(extensions=['extra', 'toc'])
-
-for fn in sys.argv[1:]:
-    with open(fn, 'r') as ifd:
-        with open('%s.html' % (fn), 'w') as ofd:
-            ofd.write((template % (md.convert(ifd.read().decode('UTF-8')))).encode('UTF-8'))
index 78e8cf0adca4dd1363fc3f3c0d1e25662f51eb7a..3b22db20aa155e7e33f6a45e4d7b783f55a7ad73 100644 (file)
@@ -131,13 +131,14 @@ static int interface_state_down(int command, struct zclient *zclient,
 extern uint32_t total_routes;
 extern uint32_t installed_routes;
 
-static int notify_owner(int command, struct zclient *zclient,
-                       zebra_size_t length, vrf_id_t vrf_id)
+static int route_notify_owner(int command, struct zclient *zclient,
+                             zebra_size_t length, vrf_id_t vrf_id)
 {
        struct prefix p;
        enum zapi_route_notify_owner note;
+       uint32_t table;
 
-       if (!zapi_route_notify_decode(zclient->ibuf, &p, &note))
+       if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note))
                return -1;
 
        installed_routes++;
@@ -210,5 +211,5 @@ void sharp_zebra_init(void)
        zclient->interface_down = interface_state_down;
        zclient->interface_address_add = interface_address_add;
        zclient->interface_address_delete = interface_address_delete;
-       zclient->notify_owner = notify_owner;
+       zclient->route_notify_owner = route_notify_owner;
 }
index 8477383469c984a06b95ffc6de866fd0dd890040..a952de89474d48061eea3b0bd3769c1c5a2d7cf4 100755 (executable)
@@ -55,7 +55,6 @@ my $min_conf_desc_length = 4;
 my $spelling_file = "$D/spelling.txt";
 my $codespell = 0;
 my $codespellfile = "/usr/share/codespell/dictionary.txt";
-my $conststructsfile = "$D/const_structs.checkpatch";
 my $typedefsfile = "";
 my $color = "auto";
 my $allow_c99_comments = 1;
@@ -685,10 +684,6 @@ sub read_words {
        return 0;
 }
 
-my $const_structs = "";
-read_words(\$const_structs, $conststructsfile)
-    or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
-
 my $typeOtherTypedefs = "";
 if (length($typedefsfile)) {
        read_words(\$typeOtherTypedefs, $typedefsfile)
@@ -2766,18 +2761,6 @@ sub process {
                        $rpt_cleaners = 1;
                }
 
-# Check for FSF mailing addresses.
-               if ($rawline =~ /\bwrite to the Free/i ||
-                   $rawline =~ /\b675\s+Mass\s+Ave/i ||
-                   $rawline =~ /\b59\s+Temple\s+Pl/i ||
-                   $rawline =~ /\b51\s+Franklin\s+St/i) {
-                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
-                       my $msg_level = \&ERROR;
-                       $msg_level = \&CHK if ($file);
-                       &{$msg_level}("FSF_MAILING_ADDRESS",
-                                     "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
-               }
-
 # check for Kconfig help text having a real description
 # Only applies when adding the entry originally, after that we do not have
 # sufficient context to determine whether it is indeed long enough.
@@ -4058,6 +4041,10 @@ sub process {
                        # likely a typedef for a function.
                        } elsif ($ctx =~ /$Type$/) {
 
+                       # All-uppercase function names are usually macros,
+                       # ignore those
+                       } elsif ($name eq uc $name) {
+
                        } else {
                                if (WARN("SPACING",
                                         "space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
@@ -6205,14 +6192,6 @@ sub process {
                             "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
                }
 
-# check for various structs that are normally const (ops, kgdb, device_tree)
-# and avoid what seem like struct definitions 'struct foo {'
-               if ($line !~ /\bconst\b/ &&
-                   $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
-                       WARN("CONST_STRUCT",
-                            "struct $1 should normally be const\n" . $herecurr);
-               }
-
 # use of NR_CPUS is usually wrong
 # ignore definitions of NR_CPUS and usage to define arrays as likely right
                if ($line =~ /\bNR_CPUS\b/ &&
index 3536df79a9916f8c6cb0e8248a7dc38bbff7da18..e6110f0c7ac6f16a2fa3889354e2e69d3a6029f3 100755 (executable)
@@ -45,10 +45,19 @@ echo $mod
 if [ -z "$mod" ]; then
   echo "There doesn't seem to be any changes."
 else
-  cp $tree/$mod /tmp/f1/
+  echo "Copying source to temp directory..."
+  for file in $mod; do
+    echo "$tree/$file --> /tmp/f1/$file"
+    cp $tree/$file /tmp/f1/
+  done
   git -C $tree reset --hard
   git -C $tree clean -fd
-  cp $tree/$mod /tmp/f2/
+  for file in $mod; do
+    if [ -f $tree/$file ]; then
+      echo "$tree/$file --> /tmp/f2/$file"
+      cp $tree/$file /tmp/f2/
+    fi
+  done
   echo "Running style checks..."
   for file in /tmp/f1/*; do
     echo "$checkpatch $file > $file _cp"
@@ -60,15 +69,20 @@ else
   done
   echo "Done."
   for file in /tmp/f1/*_cp; do
-    echo "Report for $(basename $file _cp)"
-    echo "==============================================="
     if [ -a /tmp/f2/$(basename $file) ]; then
-      diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
+      result=$(diff $file /tmp/f2/$(basename $file) | grep -A3 "ERROR\|WARNING" | grep -A2 -B2 '/tmp/f1')
     else
-      cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
+      result=$(cat $file | grep -A3 "ERROR\|WARNING" | grep -A2 -B2 '/tmp/f1')
     fi
     if [ "$?" -eq "0" ]; then
-      stat=1
+      echo "Report for $(basename $file _cp)" 1>&2
+      echo "===============================================" 1>&2
+      echo "$result" 1>&2
+      if echo $result | grep -q "ERROR"; then
+        stat=2
+      elif [ "$stat" -eq "0" ]; then
+        stat=1
+      fi
     fi
   done
 fi
diff --git a/tools/git-reindent-branch.py b/tools/git-reindent-branch.py
new file mode 100644 (file)
index 0000000..c207f59
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys, os
+import subprocess, argparse, tempfile
+import indent
+
+def run(cmd):
+    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE)
+    rv = proc.communicate('')[0].decode('UTF-8')
+    proc.wait()
+    return rv
+
+clangfmt = run(['git', 'show', 'master:.clang-format'])
+
+argp = argparse.ArgumentParser(description = 'git whitespace-fixing tool')
+argp.add_argument('branch', metavar='BRANCH', type = str, nargs = '?', default = 'HEAD')
+args = argp.parse_args()
+
+branch = args.branch
+commit   = run(['git', 'rev-list', '-n', '1', branch, '--']).strip()
+
+# frr-3.1-dev = first commit that is on master but not on stable/3.0
+masterid = run(['git', 'rev-list', '-n', '1', 'frr-3.1-dev', '--']).strip()
+masterbase = run(['git', 'merge-base', commit, masterid]).strip()
+
+if masterbase == masterid:
+    refbranch = 'master'
+else:
+    refbranch = '3.0'
+
+sys.stderr.write('autodetected base: %s (can be 3.0 or master)\n' % refbranch)
+
+beforeid = run(['git', 'rev-list', '-n', '1', 'reindent-%s-before' % refbranch, '--']).strip()
+afterid  = run(['git', 'rev-list', '-n', '1', 'reindent-%s-after' % refbranch, '--']).strip()
+
+beforebase = run(['git', 'merge-base', commit, beforeid]).strip()
+afterbase  = run(['git', 'merge-base', commit, afterid]).strip()
+
+if afterbase == afterid:
+    sys.stderr.write('this branch was already rebased\n')
+    sys.exit(1)
+
+if beforebase != beforeid:
+    sys.stderr.write('you need to rebase your branch onto the tag "reindent-%s-before"\n' % refbranch)
+    sys.exit(1)
+
+revs = run(['git', 'rev-list', 'reindent-%s-before..%s' % (refbranch, commit)]).strip().split('\n')
+revs.reverse()
+
+srcdir = os.getcwd()
+tmpdir = tempfile.mkdtemp('frrindent')
+os.chdir(tmpdir)
+
+sys.stderr.write('using temporary directory %s; %d revisions\n' % (tmpdir, len(revs)))
+run(['git', 'clone', '-s', '-b', 'reindent-%s-after' % refbranch, srcdir, 'repo'])
+os.chdir('repo')
+
+with open('.clang-format', 'w') as fd:
+    fd.write(clangfmt)
+
+prev = beforeid
+for rev in revs:
+    filestat = run(['git', 'diff', '-z', '--name-status', prev, rev]).rstrip('\0').split('\0')
+    changes = zip(filestat[0::2], filestat[1::2])
+    sys.stderr.write('%s: %d files\n' % (rev, len(changes)))
+
+    for typ, name in changes:
+        if typ == 'D':
+            run(['git', 'rm', name])
+        elif typ in ['A', 'M']:
+            run(['git', 'checkout', rev, '--', name])
+            if name.endswith('.c') or name.endswith('.h'):
+                for d in ['babeld/', 'ldpd/', 'nhrpd/']:
+                    if name.startswith(d):
+                        break
+                else:
+                    sys.stderr.write('\t%s\n' % name)
+                    indent.wrap_file(name)
+            run(['git', 'add', name])
+
+    run(['git', 'commit', '-C', rev])
+    prev = rev
+
+run(['git', 'push', 'origin', 'HEAD:refs/heads/reindented-branch'])
+sys.stderr.write('\n\n"reindented-branch" should now be OK.\n')
+sys.stderr.write('you could use "git reset --hard reindented-branch" to set your current branch to the reindented output\n')
+sys.stderr.write('\033[31;1mplease always double-check the output\033[m\n')
+
diff --git a/tools/indent.py b/tools/indent.py
new file mode 100644 (file)
index 0000000..560c13c
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# 2017 by David Lamparter, placed in public domain
+
+import sys, re, subprocess, os
+
+# find all DEFUNs
+defun_re = re.compile(
+        r'^((DEF(UN(_NOSH|_HIDDEN)?|PY)|ALIAS)\s*\(.*?)^(?=\s*\{)',
+        re.M | re.S)
+define_re = re.compile(
+        r'((^#\s*define[^\n]+[^\\]\n)+)',
+        re.M | re.S)
+# find clang-format control that we just inserted
+clean_re = re.compile(
+        r'^/\* \$FRR indent\$ \*/\s*\n\s*/\* clang-format (on|off) \*/\s*\n',
+        re.M)
+
+def wrap_file(fn):
+    with open(fn, 'r') as fd:
+        text = fd.read()
+
+        repl = r'/* $FRR indent$ */\n/* clang-format off */\n' + \
+                r'\1' + \
+                r'/* $FRR indent$ */\n/* clang-format on */\n'
+
+        # around each DEFUN, insert an indent-on/off comment
+        text = defun_re.sub(repl, text)
+        text = define_re.sub(repl, text)
+
+        ci = subprocess.Popen(['clang-format'], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
+        stdout, ign = ci.communicate(text)
+        ci.wait()
+        if ci.returncode != 0:
+            raise IOError('clang-format returned %d' % (ci.returncode))
+
+        # remove the bits we inserted above
+        final = clean_re.sub('', stdout)
+
+        tmpname = fn + '.indent'
+        with open(tmpname, 'w') as ofd:
+            ofd.write(final)
+        os.rename(tmpname, fn)
+
+if __name__ == '__main__':
+    for fn in sys.argv[1:]:
+        wrap_file(fn)
diff --git a/tools/render_md.py b/tools/render_md.py
new file mode 100644 (file)
index 0000000..16c4bbe
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# written 2016 by David Lamparter, placed in Public Domain.
+import sys, markdown
+
+template = '''<html><head><meta charset="UTF-8"><style type="text/css">
+body { max-width: 45em; margin: auto; margin-top: 2em; margin-bottom: 2em;
+    font-family:Fira Sans,sans-serif; text-align: justify;
+    counter-reset: ch2; }
+pre, code { font-family:Fira Mono,monospace; }
+pre > code { display: block; padding:0.5em; border:1px solid black;
+    background-color:#eee; color:#000; }
+h2:before { content: counter(ch2) ". "; counter-increment: ch2; }
+h2 { clear: both; margin-top: 3em; text-decoration: underline; counter-reset: ch3; }
+h3:before { content: counter(ch2) "." counter(ch3) ". "; counter-increment: ch3; }
+h3 { clear: both; margin-top: 2em; font-weight: normal; font-style: italic; }
+h4 { font-weight: normal; font-style: italic; }
+img[alt~="float-right"] { float:right; margin-left:2em; margin-bottom:2em; }
+</style></head><body>
+%s
+</body></html>
+'''
+
+md = markdown.Markdown(extensions=['extra', 'toc'])
+
+for fn in sys.argv[1:]:
+    with open(fn, 'r') as ifd:
+        with open('%s.html' % (fn), 'w') as ofd:
+            ofd.write((template % (md.convert(ifd.read().decode('UTF-8')))).encode('UTF-8'))
index c9b6f501606416c9c1207e6c8ef8ee190e45c9bc..33d34fc0ddf8a34e5fa02efe97f6b8fe051904d7 100644 (file)
@@ -147,7 +147,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \
                  $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \
                  $(top_srcdir)/lib/vrf.c \
                  $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
-                 $(top_srcdir)/lib/ns.c \
+                 $(top_srcdir)/lib/logicalrouter.c \
                  $(top_srcdir)/zebra/interface.c \
                  $(top_srcdir)/zebra/irdp_interface.c \
                  $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \
index dedf3d1647329db314ac4ca48c4a1b1b753b57aa..6cfb51b00f02d9f28493e3e7bbcc15cbc6665ce0 100755 (executable)
@@ -87,6 +87,9 @@ foreach (@ARGV) {
         elsif ($file =~ /lib\/vrf\.c$/) {
             $protocol = "VTYSH_ALL";
         }
+        elsif ($file =~ /lib\/logicalrouter\.c$/) {
+            $protocol = "VTYSH_ALL";
+        }
         elsif ($file =~ /lib\/filter\.c$/) {
             $protocol = "VTYSH_ALL";
         }
index 94c4ba43305be2e2e461d978477913bead00ffd2..e0a0dd585deee9e47845fab37165c946620f1f25 100644 (file)
@@ -975,8 +975,8 @@ static struct cmd_node pw_node = {
        PW_NODE, "%s(config-pw)# ",
 };
 
-static struct cmd_node ns_node = {
-       NS_NODE, "%s(config-logical-router)# ",
+static struct cmd_node logicalrouter_node = {
+       LOGICALROUTER_NODE, "%s(config-logical-router)# ",
 };
 
 static struct cmd_node vrf_node = {
@@ -1508,7 +1508,7 @@ static int vtysh_exit(struct vty *vty)
                break;
        case INTERFACE_NODE:
        case PW_NODE:
-       case NS_NODE:
+       case LOGICALROUTER_NODE:
        case VRF_NODE:
        case ZEBRA_NODE:
        case BGP_NODE:
@@ -1782,16 +1782,25 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_interface_vrf_cmd, "no interface IFNAME vrf NAME",
       "Delete a pseudo interface's configuration\n"
       "Interface's name\n" VRF_CMD_HELP_STR)
 
-DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME",
+DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_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")
 {
-       vty->node = NS_NODE;
+       vty->node = LOGICALROUTER_NODE;
        return CMD_SUCCESS;
 }
 
+DEFSH(VTYSH_ZEBRA, vtysh_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")
+
 DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME",
        "Select a VRF to configure\n"
        "VRF's name\n")
@@ -1804,16 +1813,18 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_cmd, "no vrf NAME", NO_STR
       "Delete a pseudo vrf's configuration\n"
       "VRF's name\n")
 
-DEFUNSH(VTYSH_NS, vtysh_exit_ns, vtysh_exit_ns_cmd, "exit",
+DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter,
+       vtysh_exit_logicalrouter_cmd, "exit",
        "Exit current mode and down to previous mode\n")
 {
        return vtysh_exit(vty);
 }
 
-DEFUNSH(VTYSH_NS, vtysh_quit_ns, vtysh_quit_ns_cmd, "quit",
+DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter,
+       vtysh_quit_logicalrouter_cmd, "quit",
        "Exit current mode and down to previous mode\n")
 {
-       return vtysh_exit_ns(self, vty, argc, argv);
+       return vtysh_exit_logicalrouter(self, vty, argc, argv);
 }
 
 DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit",
@@ -2628,6 +2639,19 @@ ALIAS(vtysh_traceroute, vtysh_traceroute_ip_cmd, "traceroute ip WORD",
       "IP trace\n"
       "Trace route to destination address or hostname\n")
 
+DEFUN (vtysh_mtrace,
+       vtysh_mtrace_cmd,
+       "mtrace WORD",
+       "Multicast trace route to multicast source\n"
+       "Multicast trace route to multicast source address\n")
+{
+       int idx = 1;
+
+       argv_find(argv, argc, "WORD", &idx);
+       execute_command("mtracebis", 1, argv[idx]->arg, NULL);
+       return CMD_SUCCESS;
+}
+
 DEFUN (vtysh_ping6,
        vtysh_ping6_cmd,
        "ping ipv6 WORD",
@@ -3042,7 +3066,7 @@ void vtysh_init_vty(void)
        install_node(&interface_node, NULL);
        install_node(&pw_node, NULL);
        install_node(&link_params_node, NULL);
-       install_node(&ns_node, NULL);
+       install_node(&logicalrouter_node, NULL);
        install_node(&vrf_node, NULL);
        install_node(&rmap_node, NULL);
        install_node(&zebra_node, NULL);
@@ -3220,11 +3244,14 @@ void vtysh_init_vty(void)
        install_element(PW_NODE, &vtysh_exit_interface_cmd);
        install_element(PW_NODE, &vtysh_quit_interface_cmd);
 
-       install_element(NS_NODE, &vtysh_end_all_cmd);
+       install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd);
 
-       install_element(CONFIG_NODE, &vtysh_ns_cmd);
-       install_element(NS_NODE, &vtysh_exit_ns_cmd);
-       install_element(NS_NODE, &vtysh_quit_ns_cmd);
+       install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd);
+       install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd);
+       install_element(LOGICALROUTER_NODE,
+                       &vtysh_exit_logicalrouter_cmd);
+       install_element(LOGICALROUTER_NODE,
+                       &vtysh_quit_logicalrouter_cmd);
 
        install_element(VRF_NODE, &vtysh_end_all_cmd);
        install_element(VRF_NODE, &vtysh_exit_vrf_cmd);
@@ -3327,6 +3354,7 @@ void vtysh_init_vty(void)
        install_element(VIEW_NODE, &vtysh_ping_ip_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd);
+       install_element(VIEW_NODE, &vtysh_mtrace_cmd);
        install_element(VIEW_NODE, &vtysh_ping6_cmd);
        install_element(VIEW_NODE, &vtysh_traceroute6_cmd);
 #if defined(HAVE_SHELL_ACCESS)
index 967f855fbc45fff1c96df49aea448196457a721f..aa1dd407eb5c3557253231eeb83459d7743544ce 100644 (file)
@@ -187,7 +187,7 @@ void vtysh_config_parse_line(void *arg, const char *line)
                                config->index = INTERFACE_NODE;
                        } else if (config->index == RMAP_NODE
                                   || config->index == INTERFACE_NODE
-                                  || config->index == NS_NODE
+                                  || config->index == LOGICALROUTER_NODE
                                   || config->index == VTY_NODE
                                   || config->index == VRF_NODE)
                                config_add_line_uniq(config->line, line);
@@ -202,7 +202,7 @@ void vtysh_config_parse_line(void *arg, const char *line)
                else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0)
                        config = config_get(PW_NODE, line);
                else if (strncmp(line, "logical-router", strlen("ns")) == 0)
-                       config = config_get(NS_NODE, line);
+                       config = config_get(LOGICALROUTER_NODE, line);
                else if (strncmp(line, "vrf", strlen("vrf")) == 0)
                        config = config_get(VRF_NODE, line);
                else if (strncmp(line, "router-id", strlen("router-id")) == 0)
index a4985c423ca36aca279c5a84b02fdd925e5f9c0a..ca6c7798ebc8ac54d71e3271cdc6cacd4e9217ef 100644 (file)
@@ -527,7 +527,7 @@ int main(int argc, char **argv, char **env)
        homedir = vtysh_get_home();
        if (homedir) {
                snprintf(history_file, sizeof(history_file),
-                        "%s/.history_quagga", homedir);
+                        "%s/.history_frr", homedir);
                if (read_history(history_file) != 0) {
                        int fp;
 
index 1d108886de669532f3f7dcbb166b30ac611c9b07..09fc0850181f0e6dd8d8342f6979596acaa7d430 100644 (file)
@@ -146,7 +146,7 @@ static int if_get_hwaddr(struct interface *ifp)
        ifreq.ifr_addr.sa_family = AF_INET;
 
        /* Fetch Hardware address if available. */
-       ret = if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq);
+       ret = vrf_if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq, ifp->vrf_id);
        if (ret < 0)
                ifp->hw_addr_len = 0;
        else {
index 14905b738bf537929eb5ebb1944472dabd19b833..639f70a6b4c3d7c9776be3991d968e7ffafe7612 100644 (file)
@@ -66,6 +66,7 @@
 #include "zebra/kernel_netlink.h"
 #include "zebra/if_netlink.h"
 
+extern struct zebra_privs_t zserv_privs;
 
 /* Note: on netlink systems, there should be a 1-to-1 mapping between interface
    names and ifindex values. */
@@ -344,12 +345,13 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb,
        }
 }
 
-static int get_iflink_speed(const char *ifname)
+static int get_iflink_speed(struct interface *interface)
 {
        struct ifreq ifdata;
        struct ethtool_cmd ecmd;
        int sd;
        int rc;
+       const char *ifname = interface->name;
 
        /* initialize struct */
        memset(&ifdata, 0, sizeof(ifdata));
@@ -363,16 +365,20 @@ static int get_iflink_speed(const char *ifname)
        ifdata.ifr_data = (__caddr_t)&ecmd;
 
        /* use ioctl to get IP address of an interface */
-       sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+       if (zserv_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
+       sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP,
+                       interface->vrf_id, NULL);
        if (sd < 0) {
                if (IS_ZEBRA_DEBUG_KERNEL)
                        zlog_debug("Failure to read interface %s speed: %d %s",
                                   ifname, errno, safe_strerror(errno));
                return 0;
        }
-
        /* Get the current link state for the interface */
-       rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata);
+       rc = vrf_ioctl(interface->vrf_id, sd, SIOCETHTOOL, (char *)&ifdata);
+       if (zserv_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
        if (rc < 0) {
                if (IS_ZEBRA_DEBUG_KERNEL)
                        zlog_debug(
@@ -389,7 +395,7 @@ static int get_iflink_speed(const char *ifname)
 
 uint32_t kernel_get_speed(struct interface *ifp)
 {
-       return get_iflink_speed(ifp->name);
+       return get_iflink_speed(ifp);
 }
 
 static int netlink_extract_bridge_info(struct rtattr *link_data,
@@ -615,13 +621,14 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h,
        }
 
        /* If VRF, create the VRF structure itself. */
-       if (zif_type == ZEBRA_IF_VRF) {
+       if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) {
                netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
                vrf_id = (vrf_id_t)ifi->ifi_index;
        }
 
        if (tb[IFLA_MASTER]) {
-               if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) {
+               if (slave_kind && (strcmp(slave_kind, "vrf") == 0)
+                   && !vrf_is_backend_netns()) {
                        zif_slave_type = ZEBRA_IF_SLAVE_VRF;
                        vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
                } else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) {
@@ -631,6 +638,8 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h,
                } else
                        zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
        }
+       if (vrf_is_backend_netns())
+               vrf_id = (vrf_id_t)ns_id;
 
        /* If linking to another interface, note it. */
        if (tb[IFLA_LINK])
@@ -644,7 +653,7 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h,
                SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
        ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]);
        ifp->metric = 0;
-       ifp->speed = get_iflink_speed(name);
+       ifp->speed = get_iflink_speed(ifp);
        ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
 
        if (desc)
@@ -789,8 +798,12 @@ static int netlink_address(int cmd, int family, struct interface *ifp,
                char buf[NL_PKT_BUF_SIZE];
        } req;
 
-       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+       struct zebra_ns *zns;
 
+       if (vrf_is_backend_netns())
+               zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id);
+       else
+               zns = zebra_ns_lookup(NS_DEFAULT);
        p = ifc->address;
        memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE);
 
@@ -1017,6 +1030,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
        zns = zebra_ns_lookup(ns_id);
        ifi = NLMSG_DATA(h);
 
+       /* assume if not default zns, then new VRF */
        if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) {
                /* If this is not link add/delete message so print warning. */
                zlog_warn("netlink_link_change: wrong kernel message %d",
@@ -1074,7 +1088,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
        }
 
        /* If VRF, create or update the VRF structure itself. */
-       if (zif_type == ZEBRA_IF_VRF) {
+       if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) {
                netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
                vrf_id = (vrf_id_t)ifi->ifi_index;
        }
@@ -1091,7 +1105,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
 
        if (h->nlmsg_type == RTM_NEWLINK) {
                if (tb[IFLA_MASTER]) {
-                       if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) {
+                       if (slave_kind && (strcmp(slave_kind, "vrf") == 0)
+                           && !vrf_is_backend_netns()) {
                                zif_slave_type = ZEBRA_IF_SLAVE_VRF;
                                vrf_id =
                                        *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
@@ -1103,7 +1118,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
                        } else
                                zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
                }
-
+               if (vrf_is_backend_netns())
+                       vrf_id = (vrf_id_t)ns_id;
                if (ifp == NULL
                    || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
                        /* Add interface notification from kernel */
index 07570e64bfc2c32f5d045aee3396cd587fdd8a43..7229b8818d84d82d9e13ccf35af7b26cdbbf94ad 100644 (file)
@@ -512,9 +512,15 @@ static void if_addr_wakeup(struct interface *ifp)
 void if_add_update(struct interface *ifp)
 {
        struct zebra_if *if_data;
+       struct zebra_ns *zns;
+       struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id);
 
-       if_link_per_ns(zebra_ns_lookup(NS_DEFAULT), ifp);
-
+       /* case interface populate before vrf enabled */
+       if (zvrf->zns)
+               zns = zvrf->zns;
+       else
+               zns = zebra_ns_lookup(NS_DEFAULT);
+       if_link_per_ns(zns, ifp);
        if_data = ifp->info;
        assert(if_data);
 
@@ -800,10 +806,12 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
        char buf[16] = "169.254.0.1";
        struct in_addr ipv4_ll;
        char mac[6];
+       ns_id_t ns_id;
 
        inet_pton(AF_INET, buf, &ipv4_ll);
 
        ipv6_ll_address_to_mac(address, (u_char *)mac);
+       ns_id = zvrf->zns->ns_id;
 
        /*
         * Remove existed arp record for the interface as netlink
@@ -811,10 +819,10 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
         *
         * supported message types are RTM_NEWNEIGH and RTM_DELNEIGH
         */
-       kernel_neigh_update (0, ifp->ifindex, ipv4_ll.s_addr, mac, 6);
+       kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id);
 
        /* Add arp record */
-       kernel_neigh_update (add, ifp->ifindex, ipv4_ll.s_addr, mac, 6);
+       kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id);
        zvrf->neigh_updates++;
 }
 
@@ -1404,7 +1412,7 @@ DEFUN (show_interface_name_vrf,
        int idx_ifname = 2;
        int idx_name = 4;
        struct interface *ifp;
-       vrf_id_t vrf_id = VRF_DEFAULT;
+       vrf_id_t vrf_id;
 
        interface_update_stats();
 
index 8e3a1d1a036307ea1d024dda8124579556a8dc42..d07d37056ec43a3d8bc173aac944a9245d93620a 100644 (file)
@@ -59,6 +59,7 @@ int if_ioctl(u_long request, caddr_t buffer)
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) {
                int save_errno = errno;
+
                if (zserv_privs.change(ZPRIVS_LOWER))
                        zlog_err("Can't lower privileges");
                zlog_err("Cannot create UDP socket: %s",
@@ -78,6 +79,39 @@ int if_ioctl(u_long request, caddr_t buffer)
        return 0;
 }
 
+/* call ioctl system call */
+int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id)
+{
+       int sock;
+       int ret;
+       int err = 0;
+
+       if (zserv_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
+       sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL);
+       if (sock < 0) {
+               int save_errno = errno;
+
+               if (zserv_privs.change(ZPRIVS_LOWER))
+                       zlog_err("Can't lower privileges");
+               zlog_err("Cannot create UDP socket: %s",
+                        safe_strerror(save_errno));
+               exit(1);
+       }
+       ret = vrf_ioctl(vrf_id, sock, request, buffer);
+       if (ret < 0)
+               err = errno;
+       if (zserv_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
+       close(sock);
+
+       if (ret < 0) {
+               errno = err;
+               return ret;
+       }
+       return 0;
+}
+
 #ifndef HAVE_NETLINK
 static int if_ioctl_ipv6(u_long request, caddr_t buffer)
 {
@@ -90,6 +124,7 @@ static int if_ioctl_ipv6(u_long request, caddr_t buffer)
        sock = socket(AF_INET6, SOCK_DGRAM, 0);
        if (sock < 0) {
                int save_errno = errno;
+
                if (zserv_privs.change(ZPRIVS_LOWER))
                        zlog_err("Can't lower privileges");
                zlog_err("Cannot create IPv6 datagram socket: %s",
@@ -122,7 +157,7 @@ void if_get_metric(struct interface *ifp)
 
        ifreq_set_name(&ifreq, ifp);
 
-       if (if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq) < 0)
+       if (vrf_if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq, ifp->vrf_id) < 0)
                return;
        ifp->metric = ifreq.ifr_metric;
        if (ifp->metric == 0)
@@ -140,7 +175,7 @@ void if_get_mtu(struct interface *ifp)
        ifreq_set_name(&ifreq, ifp);
 
 #if defined(SIOCGIFMTU)
-       if (if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq) < 0) {
+       if (vrf_if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq, ifp->vrf_id) < 0) {
                zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU)");
                ifp->mtu6 = ifp->mtu = -1;
                return;
@@ -376,9 +411,9 @@ void if_get_flags(struct interface *ifp)
 
        ifreq_set_name(&ifreq, ifp);
 
-       ret = if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq);
+       ret = vrf_if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id);
        if (ret < 0) {
-               zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s",
+               zlog_err("vrf_if_ioctl(SIOCGIFFLAGS) failed: %s",
                         safe_strerror(errno));
                return;
        }
@@ -423,7 +458,7 @@ int if_set_flags(struct interface *ifp, uint64_t flags)
        ifreq.ifr_flags = ifp->flags;
        ifreq.ifr_flags |= flags;
 
-       ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq);
+       ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id);
 
        if (ret < 0) {
                zlog_info("can't set interface flags");
@@ -444,7 +479,7 @@ int if_unset_flags(struct interface *ifp, uint64_t flags)
        ifreq.ifr_flags = ifp->flags;
        ifreq.ifr_flags &= ~flags;
 
-       ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq);
+       ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id);
 
        if (ret < 0) {
                zlog_info("can't unset interface flags");
index 02f8e6b880127cbe375da1eafd7e68b9cc220c9f..1a6e14ed4dcd9dce576549607b7e2ec3eebec7a9 100644 (file)
@@ -25,6 +25,7 @@
 /* Prototypes. */
 extern void ifreq_set_name(struct ifreq *, struct interface *);
 extern int if_ioctl(u_long, caddr_t);
+extern int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id);
 
 extern int if_set_flags(struct interface *, uint64_t);
 extern int if_unset_flags(struct interface *, uint64_t);
index e8b65925f881815743187831e6ceafaca8a32630..f429c42440a95165bf2db52772c2f30fbf3c95b1 100644 (file)
@@ -30,6 +30,7 @@
 #include "log.h"
 #include "privs.h"
 #include "vty.h"
+#include "vrf.h"
 
 #include "zebra/rib.h"
 #include "zebra/rt.h"
@@ -44,6 +45,11 @@ void lifreq_set_name(struct lifreq *lifreq, const char *ifname)
        strncpy(lifreq->lifr_name, ifname, IFNAMSIZ);
 }
 
+int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id)
+{
+       return if_ioctl(request, buffer);
+}
+
 /* call ioctl system call */
 int if_ioctl(u_long request, caddr_t buffer)
 {
index 1be2cbcaf54ea27b974cacfee3a58f9cb9a2bd5b..0b3b6eed45761b5db16ce36e597f6187ba5d0978 100644 (file)
@@ -188,7 +188,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
                return -1;
        }
 
-       sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
        if (sock < 0) {
                zlog_err("Can't open %s socket: %s", nl->name,
                         safe_strerror(errno));
index 4d888d80695ceb576a912c76dfc2e3d45f7a263a..3b28a9b2426a2d23ee838092f333e98e99a34415 100644 (file)
@@ -1384,7 +1384,8 @@ static void routing_socket(struct zebra_ns *zns)
        if (zserv_privs.change(ZPRIVS_RAISE))
                zlog_err("routing_socket: Can't raise privileges");
 
-       routing_sock = socket(AF_ROUTE, SOCK_RAW, 0);
+       routing_sock = ns_socket(AF_ROUTE, SOCK_RAW,
+                                0, (ns_id_t)zns->ns->ns_id);
 
        if (routing_sock < 0) {
                if (zserv_privs.change(ZPRIVS_LOWER))
index 19b16936d9e08900f00351249b8f5dc2eac4f568..749d509a86b4cbe22300903f7996524586642d33 100644 (file)
@@ -34,6 +34,7 @@
 #include "privs.h"
 #include "sigevent.h"
 #include "vrf.h"
+#include "logicalrouter.h"
 #include "libfrr.h"
 
 #include "zebra/rib.h"
@@ -47,6 +48,7 @@
 #include "zebra/redistribute.h"
 #include "zebra/zebra_mpls.h"
 #include "zebra/label_manager.h"
+#include "zebra/zebra_netns_notify.h"
 
 #define ZEBRA_PTM_SUPPORT
 
@@ -85,6 +87,7 @@ struct option longopts[] = {{"batch", no_argument, NULL, 'b'},
                            {"label_socket", no_argument, NULL, 'l'},
                            {"retain", no_argument, NULL, 'r'},
 #ifdef HAVE_NETLINK
+                           {"vrfwnetns", no_argument, NULL, 'n'},
                            {"nl-bufsize", required_argument, NULL, 's'},
 #endif /* HAVE_NETLINK */
                            {0}};
@@ -122,7 +125,6 @@ static void sigint(void)
 {
        struct vrf *vrf;
        struct zebra_vrf *zvrf;
-       struct zebra_ns *zns;
 
        zlog_notice("Terminating on signal");
 
@@ -137,10 +139,12 @@ static void sigint(void)
                        if (zvrf)
                                SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN);
                }
+       if (zebrad.lsp_process_q)
+               work_queue_free(zebrad.lsp_process_q);
        vrf_terminate();
 
-       zns = zebra_ns_lookup(NS_DEFAULT);
-       zebra_ns_disable(0, (void **)&zns);
+       ns_walk_func(zebra_ns_disabled);
+       zebra_ns_notify_close();
 
        access_list_reset();
        prefix_list_reset();
@@ -148,8 +152,6 @@ static void sigint(void)
 
        list_delete_and_null(&zebrad.client_list);
        work_queue_free(zebrad.ribq);
-       if (zebrad.lsp_process_q)
-               work_queue_free(zebrad.lsp_process_q);
        meta_queue_free(zebrad.mq);
 
        frr_fini();
@@ -205,12 +207,16 @@ int main(int argc, char **argv)
        char *fuzzing = NULL;
 #endif
 
+       vrf_configure_backend(VRF_BACKEND_VRF_LITE);
+       logicalrouter_configure_backend(
+                        LOGICALROUTER_BACKEND_NETNS);
+
        frr_preinit(&zebra_di, argc, argv);
 
        frr_opt_add(
                "bakz:e:l:r"
 #ifdef HAVE_NETLINK
-               "s:"
+               "s:n"
 #endif
 #if defined(HANDLE_ZAPI_FUZZING)
                "c:"
@@ -225,6 +231,7 @@ int main(int argc, char **argv)
                "  -k, --keep_kernel  Don't delete old routes which installed by zebra.\n"
                "  -r, --retain       When program terminates, retain added route by zebra.\n"
 #ifdef HAVE_NETLINK
+               "  -n, --vrfwnetns    Set VRF with NetNS\n"
                "  -s, --nl-bufsize   Set netlink receive buffer size\n"
 #endif /* HAVE_NETLINK */
 #if defined(HANDLE_ZAPI_FUZZING)
@@ -279,6 +286,11 @@ int main(int argc, char **argv)
                case 's':
                        nl_rcvbufsize = atoi(optarg);
                        break;
+               case 'n':
+                       vrf_configure_backend(VRF_BACKEND_NETNS);
+                       logicalrouter_configure_backend(
+                                       LOGICALROUTER_BACKEND_OFF);
+                       break;
 #endif /* HAVE_NETLINK */
 #if defined(HANDLE_ZAPI_FUZZING)
                case 'c':
index 54d45b889a303a9f4c9b014340bb7257b8c869a1..472f2d7a973c025dbc27e735759bf27374b6a8de 100644 (file)
@@ -78,7 +78,8 @@ extern int kernel_address_add_ipv4(struct interface *, struct connected *);
 extern int kernel_address_delete_ipv4(struct interface *, struct connected *);
 extern int kernel_address_add_ipv6 (struct interface *, struct connected *);
 extern int kernel_address_delete_ipv6 (struct interface *, struct connected *);
-extern int kernel_neigh_update(int, int, uint32_t, char *, int);
+extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr,
+                              char *lla, int llalen, ns_id_t ns_id);
 extern int kernel_interface_set_master(struct interface *master,
                                       struct interface *slave);
 
index d7789901419535b337546176f94002b4f0afcfde..e26109badf38363b5478558e5dfc6ec8e7402d53 100644 (file)
@@ -194,16 +194,25 @@ static inline int proto2zebra(int proto, int family)
 /*
 Pending: create an efficient table_id (in a tree/hash) based lookup)
  */
-static vrf_id_t vrf_lookup_by_table(u_int32_t table_id)
+static vrf_id_t vrf_lookup_by_table(u_int32_t table_id, ns_id_t ns_id)
 {
        struct vrf *vrf;
        struct zebra_vrf *zvrf;
 
        RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
-               if ((zvrf = vrf->info) == NULL || (zvrf->table_id != table_id))
+               zvrf = vrf->info;
+               if (zvrf == NULL)
                        continue;
-
-               return zvrf_id(zvrf);
+               /* case vrf with netns : match the netnsid */
+               if (vrf_is_backend_netns()) {
+                       if (ns_id == zvrf_id(zvrf))
+                               return zvrf_id(zvrf);
+               } else {
+                       /* VRF is VRF_BACKEND_VRF_LITE */
+                       if (zvrf->table_id != table_id)
+                               continue;
+                       return zvrf_id(zvrf);
+               }
        }
 
        return VRF_DEFAULT;
@@ -220,7 +229,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
        u_char flags = 0;
        struct prefix p;
        struct prefix_ipv6 src_p = {};
-       vrf_id_t vrf_id = VRF_DEFAULT;
+       vrf_id_t vrf_id;
 
        char anyaddr[16] = {0};
 
@@ -288,7 +297,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
                table = rtm->rtm_table;
 
        /* Map to VRF */
-       vrf_id = vrf_lookup_by_table(table);
+       vrf_id = vrf_lookup_by_table(table, ns_id);
        if (vrf_id == VRF_DEFAULT) {
                if (!is_zebra_valid_kernel_table(table)
                    && !is_zebra_main_routing_table(table))
@@ -609,7 +618,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl,
        char sbuf[40];
        char gbuf[40];
        char oif_list[256] = "\0";
-       vrf_id_t vrf = ns_id;
+       vrf_id_t vrf;
        int table;
 
        if (mroute)
@@ -631,7 +640,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl,
        else
                table = rtm->rtm_table;
 
-       vrf = vrf_lookup_by_table(table);
+       vrf = vrf_lookup_by_table(table, ns_id);
 
        if (tb[RTA_IIF])
                iif = *(int *)RTA_DATA(tb[RTA_IIF]);
@@ -687,24 +696,23 @@ int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
                         ns_id_t ns_id, int startup)
 {
        int len;
-       vrf_id_t vrf_id = ns_id;
        struct rtmsg *rtm;
 
        rtm = NLMSG_DATA(h);
 
        if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) {
                /* If this is not route add/delete message print warning. */
-               zlog_warn("Kernel message: %d vrf %u\n", h->nlmsg_type, vrf_id);
+               zlog_warn("Kernel message: %d NS %u\n", h->nlmsg_type, ns_id);
                return 0;
        }
 
        /* Connected route. */
        if (IS_ZEBRA_DEBUG_KERNEL)
-               zlog_debug("%s %s %s proto %s vrf %u",
+               zlog_debug("%s %s %s proto %s NS %u",
                           nl_msg_type_to_str(h->nlmsg_type),
                           nl_family_to_str(rtm->rtm_family),
                           nl_rttype_to_str(rtm->rtm_type),
-                          nl_rtproto_to_str(rtm->rtm_protocol), vrf_id);
+                          nl_rtproto_to_str(rtm->rtm_protocol), ns_id);
 
        /* We don't care about change notifications for the MPLS table. */
        /* TODO: Revisit this. */
@@ -840,6 +848,7 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen,
 {
        struct mpls_label_stack *nh_label;
        mpls_lse_t out_lse[MPLS_MAX_LABELS];
+       int num_labels = 0;
        char label_buf[256];
 
        /*
@@ -849,56 +858,54 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen,
         * you fix this assumption
         */
        label_buf[0] = '\0';
-       /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP
-        * (in the case of LER)
-        */
-       nh_label = nexthop->nh_label;
-       if (rtmsg->rtm_family == AF_MPLS) {
-               assert(nh_label);
-               assert(nh_label->num_labels == 1);
-       }
 
-       if (nh_label && nh_label->num_labels) {
-               int i, num_labels = 0;
-               u_int32_t bos;
+       assert(nexthop);
+       for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) {
                char label_buf1[20];
 
-               for (i = 0; i < nh_label->num_labels; i++) {
-                       if (nh_label->label[i] != MPLS_LABEL_IMPLICIT_NULL) {
-                               bos = ((i == (nh_label->num_labels - 1)) ? 1
-                                                                        : 0);
-                               out_lse[i] = mpls_lse_encode(nh_label->label[i],
-                                                            0, 0, bos);
-                               if (IS_ZEBRA_DEBUG_KERNEL) {
-                                       if (!num_labels)
-                                               sprintf(label_buf, "label %u",
-                                                       nh_label->label[i]);
-                                       else {
-                                               sprintf(label_buf1, "/%u",
-                                                       nh_label->label[i]);
-                                               strlcat(label_buf, label_buf1,
-                                                       sizeof(label_buf));
-                                       }
+               nh_label = nh->nh_label;
+               if (!nh_label || !nh_label->num_labels)
+                       continue;
+
+               for (int i = 0; i < nh_label->num_labels; i++) {
+                       if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
+                               continue;
+
+                       if (IS_ZEBRA_DEBUG_KERNEL) {
+                               if (!num_labels)
+                                       sprintf(label_buf, "label %u",
+                                               nh_label->label[i]);
+                               else {
+                                       sprintf(label_buf1, "/%u",
+                                               nh_label->label[i]);
+                                       strlcat(label_buf, label_buf1,
+                                               sizeof(label_buf));
                                }
-                               num_labels++;
                        }
+
+                       out_lse[num_labels] =
+                               mpls_lse_encode(nh_label->label[i], 0, 0, 0);
+                       num_labels++;
                }
-               if (num_labels) {
-                       if (rtmsg->rtm_family == AF_MPLS)
-                               addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse,
-                                         num_labels * sizeof(mpls_lse_t));
-                       else {
-                               struct rtattr *nest;
-                               u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
-
-                               addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE,
-                                         &encap, sizeof(u_int16_t));
-                               nest = addattr_nest(nlmsg, req_size, RTA_ENCAP);
-                               addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST,
-                                         &out_lse,
-                                         num_labels * sizeof(mpls_lse_t));
-                               addattr_nest_end(nlmsg, nest);
-                       }
+       }
+
+       if (num_labels) {
+               /* Set the BoS bit */
+               out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT);
+
+               if (rtmsg->rtm_family == AF_MPLS)
+                       addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse,
+                                 num_labels * sizeof(mpls_lse_t));
+               else {
+                       struct rtattr *nest;
+                       u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
+
+                       addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, &encap,
+                                 sizeof(u_int16_t));
+                       nest = addattr_nest(nlmsg, req_size, RTA_ENCAP);
+                       addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST, &out_lse,
+                                 num_labels * sizeof(mpls_lse_t));
+                       addattr_nest_end(nlmsg, nest);
                }
        }
 
@@ -1045,6 +1052,7 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen,
 {
        struct mpls_label_stack *nh_label;
        mpls_lse_t out_lse[MPLS_MAX_LABELS];
+       int num_labels = 0;
        char label_buf[256];
 
        rtnh->rtnh_len = sizeof(*rtnh);
@@ -1059,63 +1067,60 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen,
         * you fix this assumption
         */
        label_buf[0] = '\0';
-       /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP
-        * (in the case of LER)
-        */
-       nh_label = nexthop->nh_label;
-       if (rtmsg->rtm_family == AF_MPLS) {
-               assert(nh_label);
-               assert(nh_label->num_labels == 1);
-       }
 
-       if (nh_label && nh_label->num_labels) {
-               int i, num_labels = 0;
-               u_int32_t bos;
+       assert(nexthop);
+       for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) {
                char label_buf1[20];
 
-               for (i = 0; i < nh_label->num_labels; i++) {
-                       if (nh_label->label[i] != MPLS_LABEL_IMPLICIT_NULL) {
-                               bos = ((i == (nh_label->num_labels - 1)) ? 1
-                                                                        : 0);
-                               out_lse[i] = mpls_lse_encode(nh_label->label[i],
-                                                            0, 0, bos);
-                               if (IS_ZEBRA_DEBUG_KERNEL) {
-                                       if (!num_labels)
-                                               sprintf(label_buf, "label %u",
-                                                       nh_label->label[i]);
-                                       else {
-                                               sprintf(label_buf1, "/%u",
-                                                       nh_label->label[i]);
-                                               strlcat(label_buf, label_buf1,
-                                                       sizeof(label_buf));
-                                       }
+               nh_label = nh->nh_label;
+               if (!nh_label || !nh_label->num_labels)
+                       continue;
+
+               for (int i = 0; i < nh_label->num_labels; i++) {
+                       if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
+                               continue;
+
+                       if (IS_ZEBRA_DEBUG_KERNEL) {
+                               if (!num_labels)
+                                       sprintf(label_buf, "label %u",
+                                               nh_label->label[i]);
+                               else {
+                                       sprintf(label_buf1, "/%u",
+                                               nh_label->label[i]);
+                                       strlcat(label_buf, label_buf1,
+                                               sizeof(label_buf));
                                }
-                               num_labels++;
                        }
+
+                       out_lse[num_labels] =
+                               mpls_lse_encode(nh_label->label[i], 0, 0, 0);
+                       num_labels++;
                }
-               if (num_labels) {
-                       if (rtmsg->rtm_family == AF_MPLS) {
-                               rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST,
-                                             &out_lse,
-                                             num_labels * sizeof(mpls_lse_t));
-                               rtnh->rtnh_len += RTA_LENGTH(
-                                       num_labels * sizeof(mpls_lse_t));
-                       } else {
-                               struct rtattr *nest;
-                               u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
-                               int len = rta->rta_len;
-
-                               rta_addattr_l(rta, NL_PKT_BUF_SIZE,
-                                             RTA_ENCAP_TYPE, &encap,
-                                             sizeof(u_int16_t));
-                               nest = rta_nest(rta, NL_PKT_BUF_SIZE,
-                                               RTA_ENCAP);
-                               rta_addattr_l(rta, NL_PKT_BUF_SIZE,
-                                             MPLS_IPTUNNEL_DST, &out_lse,
-                                             num_labels * sizeof(mpls_lse_t));
-                               rta_nest_end(rta, nest);
-                               rtnh->rtnh_len += rta->rta_len - len;
-                       }
+       }
+
+       if (num_labels) {
+               /* Set the BoS bit */
+               out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT);
+
+               if (rtmsg->rtm_family == AF_MPLS) {
+                       rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST,
+                                     &out_lse,
+                                     num_labels * sizeof(mpls_lse_t));
+                       rtnh->rtnh_len +=
+                               RTA_LENGTH(num_labels * sizeof(mpls_lse_t));
+               } else {
+                       struct rtattr *nest;
+                       u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
+                       int len = rta->rta_len;
+
+                       rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_ENCAP_TYPE,
+                                     &encap, sizeof(u_int16_t));
+                       nest = rta_nest(rta, NL_PKT_BUF_SIZE, RTA_ENCAP);
+                       rta_addattr_l(rta, NL_PKT_BUF_SIZE, MPLS_IPTUNNEL_DST,
+                                     &out_lse,
+                                     num_labels * sizeof(mpls_lse_t));
+                       rta_nest_end(rta, nest);
+                       rtnh->rtnh_len += rta->rta_len - len;
                }
        }
 
@@ -1277,7 +1282,7 @@ static void _netlink_mpls_debug(int cmd, u_int32_t label, const char *routedesc)
 }
 
 static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
-                               int llalen)
+                               int llalen, ns_id_t ns_id)
 {
        struct {
                struct nlmsghdr n;
@@ -1285,7 +1290,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
                char buf[256];
        } req;
 
-       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+       struct zebra_ns *zns = zebra_ns_lookup(ns_id);
 
        memset(&req.n, 0, sizeof(req.n));
        memset(&req.ndm, 0, sizeof(req.ndm));
@@ -1329,9 +1334,10 @@ static int netlink_route_multipath(int cmd, struct prefix *p,
                char buf[NL_PKT_BUF_SIZE];
        } req;
 
-       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+       struct zebra_ns *zns;
        struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id);
 
+       zns = zvrf->zns;
        memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE);
 
        bytelen = (family == AF_INET ? 4 : 16);
@@ -1629,8 +1635,9 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
        } req;
 
        mroute = mr;
-       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+       struct zebra_ns *zns;
 
+       zns = zvrf->zns;
        memset(&req.n, 0, sizeof(req.n));
        memset(&req.ndm, 0, sizeof(req.ndm));
 
@@ -1703,10 +1710,10 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p,
 }
 
 int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
-                       int llalen)
+                       int llalen, ns_id_t ns_id)
 {
        return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex,
-                                   addr, lla, llalen);
+                                   addr, lla, llalen, ns_id);
 }
 
 /*
@@ -1716,14 +1723,16 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
 static int netlink_vxlan_flood_list_update(struct interface *ifp,
                                           struct in_addr *vtep_ip, int cmd)
 {
-       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+       struct zebra_ns *zns;
        struct {
                struct nlmsghdr n;
                struct ndmsg ndm;
                char buf[256];
        } req;
        u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+       struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
 
+       zns = zvrf->zns;
        memset(&req.n, 0, sizeof(req.n));
        memset(&req.ndm, 0, sizeof(req.ndm));
 
@@ -1779,7 +1788,7 @@ int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
 #endif
 
 static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
-                                int len)
+                                int len, ns_id_t ns_id)
 {
        struct ndmsg *ndm;
        struct interface *ifp;
@@ -1802,7 +1811,7 @@ static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
                return 0;
 
        /* The interface should exist. */
-       ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
+       ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
                                        ndm->ndm_ifindex);
        if (!ifp || !ifp->info)
                return 0;
@@ -1933,7 +1942,7 @@ static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h,
        if (ndm->ndm_family != AF_BRIDGE)
                return 0;
 
-       return netlink_macfdb_change(snl, h, len);
+       return netlink_macfdb_change(snl, h, len, ns_id);
 }
 
 /* Request for MAC FDB information from the kernel */
@@ -2015,7 +2024,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
                                 struct ethaddr *mac, struct in_addr vtep_ip,
                                 int local, int cmd, u_char sticky)
 {
-       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+       struct zebra_ns *zns;
        struct {
                struct nlmsghdr n;
                struct ndmsg ndm;
@@ -2029,7 +2038,9 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
        int vid_present = 0, dst_present = 0;
        char vid_buf[20];
        char dst_buf[30];
+       struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
 
+       zns = zvrf->zns;
        zif = ifp->info;
        if ((br_if = zif->brslave_info.br_if) == NULL) {
                zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge",
@@ -2089,7 +2100,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
         | NUD_DELAY)
 
 static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
-                                 int len)
+                                 int len, ns_id_t ns_id)
 {
        struct ndmsg *ndm;
        struct interface *ifp;
@@ -2110,7 +2121,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
                return 0;
 
        /* The interface should exist. */
-       ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
+       ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
                                        ndm->ndm_ifindex);
        if (!ifp || !ifp->info)
                return 0;
@@ -2132,7 +2143,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
         * itself
         */
        if (IS_ZEBRA_IF_VLAN(ifp)) {
-               link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
+               link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
                                                    zif->link_ifindex);
                if (!link_if)
                        return 0;
@@ -2310,13 +2321,13 @@ int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
        /* Is this a notification for the MAC FDB or IP neighbor table? */
        ndm = NLMSG_DATA(h);
        if (ndm->ndm_family == AF_BRIDGE)
-               return netlink_macfdb_change(snl, h, len);
+               return netlink_macfdb_change(snl, h, len, ns_id);
 
        if (ndm->ndm_type != RTN_UNICAST)
                return 0;
 
        if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6)
-               return netlink_ipneigh_change(snl, h, len);
+               return netlink_ipneigh_change(snl, h, len, ns_id);
 
        return 0;
 }
@@ -2331,10 +2342,12 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
        } req;
        int ipa_len;
 
-       struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+       struct zebra_ns *zns;
        char buf[INET6_ADDRSTRLEN];
        char buf2[ETHER_ADDR_STRLEN];
+       struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
 
+       zns = zvrf->zns;
        memset(&req.n, 0, sizeof(req.n));
        memset(&req.ndm, 0, sizeof(req.ndm));
 
index 6d4af1203c7d457e0174d31bee188ccd7b1bf45b..b2baee572805d031dc575de912046645b785e3e4 100644 (file)
@@ -424,7 +424,7 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p,
 }
 
 int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
-                       int llalen)
+                       int llalen, ns_id_t ns_id)
 {
        /* TODO */
        return 0;
index 32418eb82fb45498d90d16505209135e1929bc39..860e8710d6899bff1d890c918a19f4b3dba79a7b 100644 (file)
@@ -34,6 +34,7 @@
 #include "command.h"
 #include "privs.h"
 #include "vrf.h"
+#include "ns.h"
 
 #include "zebra/interface.h"
 #include "zebra/rtadv.h"
@@ -621,7 +622,7 @@ static int rtadv_read(struct thread *thread)
        return 0;
 }
 
-static int rtadv_make_socket(void)
+static int rtadv_make_socket(ns_id_t ns_id)
 {
        int sock;
        int ret = 0;
@@ -631,7 +632,7 @@ static int rtadv_make_socket(void)
                zlog_err("rtadv_make_socket: could not raise privs, %s",
                         safe_strerror(errno));
 
-       sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+       sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id);
 
        if (zserv_privs.change(ZPRIVS_LOWER))
                zlog_err("rtadv_make_socket: could not lower privs, %s",
@@ -1686,7 +1687,7 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val)
 
 void rtadv_init(struct zebra_ns *zns)
 {
-       zns->rtadv.sock = rtadv_make_socket();
+       zns->rtadv.sock = rtadv_make_socket(zns->ns_id);
 }
 
 void rtadv_terminate(struct zebra_ns *zns)
index 347482362341bfc4c58ba32549e6f19ae976668d..bb7439c0f617625168abc430d8006ef32d23d668 100644 (file)
@@ -65,6 +65,8 @@ zebra_zebra_SOURCES = \
        zebra/zebra_vty.c \
        zebra/zebra_vxlan.c \
        zebra/zserv.c \
+       zebra/zebra_netns_id.c \
+       zebra/zebra_netns_notify.c \
        # end
 
 zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS)
@@ -104,6 +106,8 @@ noinst_HEADERS += \
        zebra/zebra_vxlan.h \
        zebra/zebra_vxlan_private.h \
        zebra/zserv.h \
+       zebra/zebra_netns_id.h \
+       zebra/zebra_netns_notify.h \
        # end
 
 zebra_zebra_irdp_la_SOURCES = \
diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c
new file mode 100644 (file)
index 0000000..966d6ed
--- /dev/null
@@ -0,0 +1,355 @@
+/* zebra NETNS ID handling routines
+ * those routines are implemented locally to avoid having external dependencies.
+ * Copyright (C) 2018 6WIND
+ *
+ * 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 "vrf.h"
+#include "log.h"
+
+#if defined(HAVE_NETLINK)
+
+#include <linux/net_namespace.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include "rib.h"
+#include "zebra_ns.h"
+#include "kernel_netlink.h"
+#endif /* defined(HAVE_NETLINK) */
+
+#include "zebra_netns_id.h"
+
+/* default NS ID value used when VRF backend is not NETNS */
+#define NS_DEFAULT_INTERNAL 0
+
+/* in case NEWNSID not available, the NSID will be locally obtained
+ */
+#define NS_BASE_NSID 0
+
+#if defined(HAVE_NETLINK)
+
+#define NETLINK_SOCKET_BUFFER_SIZE 512
+#define NETLINK_ALIGNTO             4
+#define NETLINK_ALIGN(len)          (((len)+NETLINK_ALIGNTO-1) \
+                                    & ~(NETLINK_ALIGNTO-1))
+#define NETLINK_NLATTR_LEN(_a, _b)   (unsigned int)((char *)_a - (char *)_b)
+
+#endif /* defined(HAVE_NETLINK) */
+
+static ns_id_t zebra_ns_id_get_fallback(const char *netnspath)
+{
+       static int zebra_ns_id_local;
+
+       return zebra_ns_id_local++;
+}
+
+#if defined(HAVE_NETLINK)
+
+static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type)
+{
+       struct nlmsghdr *nlh;
+
+       nlh = (struct nlmsghdr *)buf;
+       nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr));
+
+       nlh->nlmsg_type = type;
+       nlh->nlmsg_flags = NLM_F_REQUEST;
+       if (type == RTM_NEWNSID)
+               nlh->nlmsg_flags |= NLM_F_ACK;
+       nlh->nlmsg_seq = *seq = time(NULL);
+       return nlh;
+}
+
+static int send_receive(int sock, struct nlmsghdr *nlh,
+                       unsigned int seq, char *buf)
+{
+       int ret;
+       static const struct sockaddr_nl snl = {
+               .nl_family = AF_NETLINK
+       };
+
+       ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0,
+                    (struct sockaddr *) &snl, (socklen_t)sizeof(snl));
+       if (ret < 0) {
+               zlog_err("netlink( %u) sendmsg() error: %s",
+                        sock, safe_strerror(errno));
+               return -1;
+       }
+
+       /* reception */
+       struct sockaddr_nl addr;
+       struct iovec iov = {
+               .iov_base       = buf,
+               .iov_len        = NETLINK_SOCKET_BUFFER_SIZE,
+       };
+       struct msghdr msg = {
+               .msg_name       = &addr,
+               .msg_namelen    = sizeof(struct sockaddr_nl),
+               .msg_iov        = &iov,
+               .msg_iovlen     = 1,
+               .msg_control    = NULL,
+               .msg_controllen = 0,
+               .msg_flags      = 0,
+       };
+       ret = recvmsg(sock, &msg, 0);
+       if (ret < 0) {
+               zlog_err("netlink recvmsg: error %d (errno %u)", ret, errno);
+               return -1;
+       }
+       if (msg.msg_flags & MSG_TRUNC) {
+               zlog_err("netlink recvmsg : error message truncated");
+               return -1;
+       }
+       /* nlh already points to buf */
+       if (nlh->nlmsg_seq != seq) {
+               zlog_err("netlink recvmsg: bad sequence number %x (expected %x)",
+                        seq, nlh->nlmsg_seq);
+               return -1;
+       }
+       return ret;
+}
+
+/* extract on a valid nlmsg the nsid
+ * valid nlmsghdr - not a nlmsgerr
+ */
+static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
+{
+       ns_id_t ns_id = NS_UNKNOWN;
+       int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
+               NETLINK_ALIGN(sizeof(struct rtgenmsg));
+       int curr_length = offset;
+       void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len));
+       struct nlattr *attr;
+
+       for (attr = (struct nlattr *)((char *)buf + offset);
+            NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr) &&
+                    attr->nla_len >= sizeof(struct nlattr) &&
+                    attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr);
+            attr += NETLINK_ALIGN(attr->nla_len)) {
+               curr_length += attr->nla_len;
+               if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) {
+                       u_int32_t *ptr = (u_int32_t *)(attr);
+
+                       ns_id = ptr[1];
+                       break;
+               }
+       }
+       return ns_id;
+}
+
+ns_id_t zebra_ns_id_get(const char *netnspath)
+{
+       int ns_id = -1;
+       struct sockaddr_nl snl;
+       int fd, sock, ret;
+       unsigned int seq;
+       ns_id_t return_nsid = NS_UNKNOWN;
+
+       /* netns path check */
+       if (!netnspath)
+               return NS_UNKNOWN;
+       fd = open(netnspath, O_RDONLY);
+       if (fd == -1)
+               return NS_UNKNOWN;
+
+       /* netlink socket */
+       sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       if (sock < 0) {
+               zlog_err("netlink( %u) socket() error: %s",
+                        sock, safe_strerror(errno));
+               return NS_UNKNOWN;
+       }
+       memset(&snl, 0, sizeof(snl));
+       snl.nl_family = AF_NETLINK;
+       snl.nl_groups = RTNLGRP_NSID;
+       snl.nl_pid = 0; /* AUTO PID */
+       ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl));
+       if (ret < 0) {
+               zlog_err("netlink( %u) socket() bind error: %s",
+                        sock, safe_strerror(errno));
+               close(sock);
+               close(fd);
+               return NS_UNKNOWN;
+       }
+
+       /* message to send to netlink,and response : NEWNSID */
+       char buf[NETLINK_SOCKET_BUFFER_SIZE];
+       struct nlmsghdr *nlh;
+       struct rtgenmsg *rt;
+       int len;
+
+       memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
+       nlh = initiate_nlh(buf, &seq, RTM_NEWNSID);
+       rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
+       nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
+       rt->rtgen_family = AF_UNSPEC;
+
+       addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
+       addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
+
+       ret = send_receive(sock, nlh, seq, buf);
+       if (ret < 0) {
+               close(sock);
+               close(fd);
+               return NS_UNKNOWN;
+       }
+       nlh = (struct nlmsghdr *)buf;
+
+       /* message to analyse : NEWNSID response */
+       len = ret;
+       ret = 0;
+       do {
+               if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
+                       return_nsid = extract_nsid(nlh, buf);
+                       if (return_nsid != NS_UNKNOWN)
+                               break;
+               } else {
+                       if (nlh->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *err = (struct nlmsgerr *)
+                                       ((char *)nlh +
+                                        NETLINK_ALIGN(sizeof(
+                                               struct nlmsghdr)));
+
+                               ret = -1;
+                               if (err->error < 0)
+                                       errno = -err->error;
+                               else
+                                       errno = err->error;
+                               if (errno == 0) {
+                                       /* request NEWNSID was successfull
+                                        * return EEXIST error to get GETNSID
+                                        */
+                                       errno = EEXIST;
+                               }
+                       } else {
+                               /* other errors ignored
+                                * attempt to get nsid
+                                */
+                               ret = -1;
+                               errno = EEXIST;
+                               break;
+                       }
+               }
+               len = len - NETLINK_ALIGN(nlh->nlmsg_len);
+               nlh = (struct nlmsghdr *)((char *)nlh +
+                                         NETLINK_ALIGN(nlh->nlmsg_len));
+       } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0);
+
+       if (ret <= 0) {
+               if (errno != EEXIST && ret != 0) {
+                       zlog_err("netlink( %u) recvfrom() error 2 when reading: %s",
+                                fd, safe_strerror(errno));
+                       close(sock);
+                       close(fd);
+                       if (errno == ENOTSUP) {
+                               zlog_warn("NEWNSID locally generated");
+                               return zebra_ns_id_get_fallback(netnspath);
+                       }
+                       return NS_UNKNOWN;
+               }
+               /* message to send to netlink : GETNSID */
+               memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
+               nlh = initiate_nlh(buf, &seq, RTM_GETNSID);
+               rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
+               nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
+               rt->rtgen_family = AF_UNSPEC;
+
+               addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
+               addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
+
+               ret = send_receive(sock, nlh, seq, buf);
+               if (ret < 0) {
+                       close(sock);
+                       close(fd);
+                       return NS_UNKNOWN;
+               }
+               nlh = (struct nlmsghdr *)buf;
+               len = ret;
+               ret = 0;
+               do {
+                       if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
+                               return_nsid = extract_nsid(nlh, buf);
+                               if (return_nsid != NS_UNKNOWN)
+                                       break;
+                       } else if (nlh->nlmsg_type == NLMSG_ERROR) {
+                               struct nlmsgerr *err = (struct nlmsgerr *)
+                                       ((char *)nlh +
+                                        NETLINK_ALIGN(sizeof(
+                                                struct nlmsghdr)));
+                               if (err->error < 0)
+                                       errno = -err->error;
+                               else
+                                       errno = err->error;
+                               break;
+                       }
+                       len = len - NETLINK_ALIGN(nlh->nlmsg_len);
+                       nlh = (struct nlmsghdr *)((char *)nlh +
+                                 NETLINK_ALIGN(nlh->nlmsg_len));
+               } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0);
+       }
+
+       close(fd);
+       close(sock);
+       return return_nsid;
+}
+
+#else
+ns_id_t zebra_ns_id_get(const char *netnspath)
+{
+       return zebra_ns_id_get_fallback(netnspath);
+}
+#endif /* ! defined(HAVE_NETLINK) */
+
+#ifdef HAVE_NETNS
+static void zebra_ns_create_netns_directory(void)
+{
+       /* check that /var/run/netns is created */
+       /* S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH */
+       if (mkdir(NS_RUN_DIR, 0755)) {
+               if (errno != EEXIST) {
+                       zlog_warn("NS check: failed to access %s", NS_RUN_DIR);
+                       return;
+               }
+       }
+}
+#endif
+
+ns_id_t zebra_ns_id_get_default(void)
+{
+#ifdef HAVE_NETNS
+       int fd;
+#endif /* !HAVE_NETNS */
+
+#ifdef HAVE_NETNS
+       if (vrf_is_backend_netns())
+               zebra_ns_create_netns_directory();
+       fd = open(NS_DEFAULT_NAME, O_RDONLY);
+
+       if (fd == -1)
+               return NS_DEFAULT_INTERNAL;
+       if (!vrf_is_backend_netns())
+               return NS_DEFAULT_INTERNAL;
+       close(fd);
+       return zebra_ns_id_get((char *)NS_DEFAULT_NAME);
+#else /* HAVE_NETNS */
+       return NS_DEFAULT_INTERNAL;
+#endif /* !HAVE_NETNS */
+}
+
diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h
new file mode 100644 (file)
index 0000000..d6530e6
--- /dev/null
@@ -0,0 +1,26 @@
+/* zebra NETNS ID handling routines
+ * Copyright (C) 2018 6WIND
+ *
+ * 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(__ZEBRA_NS_ID_H__)
+#define __ZEBRA_NS_ID_H__
+#include "zebra.h"
+#include "ns.h"
+
+extern ns_id_t zebra_ns_id_get(const char *netnspath);
+extern ns_id_t zebra_ns_id_get_default(void);
+
+#endif /* __ZEBRA_NS_ID_H__ */
diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c
new file mode 100644 (file)
index 0000000..b98d6ed
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Zebra NS collector and notifier for Network NameSpaces
+ * Copyright (C) 2017 6WIND
+ *
+ * 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>
+
+#ifdef HAVE_NETLINK
+#ifdef HAVE_NETNS
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <sched.h>
+#endif
+#include <dirent.h>
+#include <sys/inotify.h>
+#include <sys/stat.h>
+
+#include "thread.h"
+#include "ns.h"
+#include "command.h"
+#include "memory.h"
+
+#include "zserv.h"
+#include "zebra_memory.h"
+#endif /* defined(HAVE_NETLINK) */
+
+#include "zebra_netns_notify.h"
+#include "zebra_netns_id.h"
+
+#ifdef HAVE_NETLINK
+
+/* upon creation of folder under /var/run/netns,
+ * wait that netns context is bound to
+ * that folder 10 seconds
+ */
+#define ZEBRA_NS_POLLING_INTERVAL_MSEC     1000
+#define ZEBRA_NS_POLLING_MAX_RETRIES  200
+
+DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo")
+static struct thread *zebra_netns_notify_current;
+
+struct zebra_netns_info {
+       const char *netnspath;
+       unsigned int retries;
+};
+
+static int zebra_ns_ready_read(struct thread *t);
+static void zebra_ns_notify_create_context_from_entry_name(const char *name);
+static int zebra_ns_continue_read(struct zebra_netns_info *zns_info,
+                                 int stop_retry);
+static int zebra_ns_notify_read(struct thread *t);
+
+static void zebra_ns_notify_create_context_from_entry_name(const char *name)
+{
+       char *netnspath = ns_netns_pathname(NULL, name);
+       struct vrf *vrf;
+       int ret;
+       ns_id_t ns_id;
+
+       if (netnspath == NULL)
+               return;
+
+       if (zserv_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
+       ns_id = zebra_ns_id_get(netnspath);
+       if (zserv_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
+       /* if VRF with NS ID already present */
+       vrf = vrf_lookup_by_id((vrf_id_t)ns_id);
+       if (vrf) {
+               zlog_warn("NS notify : same NSID used by VRF %s. Ignore NS %s creation",
+                         vrf->name, netnspath);
+               return;
+       }
+       if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) {
+               zlog_warn("NS notify : failed to create VRF %s", name);
+               return;
+       }
+       ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id);
+       if (ret != CMD_SUCCESS) {
+               zlog_warn("NS notify : failed to create NS %s", netnspath);
+               return;
+       }
+       zlog_info("NS notify : created VRF %s NS %s",
+                  name, netnspath);
+}
+
+static int zebra_ns_continue_read(struct zebra_netns_info *zns_info,
+                                 int stop_retry)
+{
+       void *ns_path_ptr = (void *)zns_info->netnspath;
+
+       if (stop_retry) {
+               XFREE(MTYPE_NETNS_MISC, ns_path_ptr);
+               XFREE(MTYPE_NETNS_MISC, zns_info);
+               return 0;
+       }
+       thread_add_timer_msec(zebrad.master, zebra_ns_ready_read,
+                             (void *)zns_info,
+                             ZEBRA_NS_POLLING_INTERVAL_MSEC, NULL);
+       return 0;
+}
+
+static int zebra_ns_ready_read(struct thread *t)
+{
+       struct zebra_netns_info *zns_info = THREAD_ARG(t);
+       const char *netnspath;
+       int err, stop_retry = 0;
+
+       if (!zns_info)
+               return 0;
+       if (!zns_info->netnspath) {
+               XFREE(MTYPE_NETNS_MISC, zns_info);
+               return 0;
+       }
+       netnspath = zns_info->netnspath;
+       if (--zns_info->retries == 0)
+               stop_retry = 1;
+       if (zserv_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
+       err = ns_switch_to_netns(netnspath);
+       if (zserv_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
+       if (err < 0)
+               return zebra_ns_continue_read(zns_info, stop_retry);
+
+       /* go back to default ns */
+       if (zserv_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
+       err = ns_switchback_to_initial();
+       if (zserv_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
+       if (err < 0)
+               return zebra_ns_continue_read(zns_info, stop_retry);
+
+       /* success : close fd and create zns context */
+       zebra_ns_notify_create_context_from_entry_name(basename(netnspath));
+       return zebra_ns_continue_read(zns_info, 1);
+}
+
+static int zebra_ns_notify_read(struct thread *t)
+{
+       int fd_monitor = THREAD_FD(t);
+       struct inotify_event *event;
+       char buf[BUFSIZ];
+       ssize_t len;
+
+       zebra_netns_notify_current = thread_add_read(zebrad.master,
+                                                    zebra_ns_notify_read,
+                                                    NULL, fd_monitor, NULL);
+       len = read(fd_monitor, buf, sizeof(buf));
+       if (len < 0) {
+               zlog_warn("NS notify read: failed to read (%s)",
+                         safe_strerror(errno));
+               return 0;
+       }
+       for (event = (struct inotify_event *)buf;
+            (char *)event < &buf[len];
+            event = (struct inotify_event *)((char *)event +
+                                             sizeof(*event) + event->len)) {
+               char *netnspath;
+               struct zebra_netns_info *netnsinfo;
+
+               if (!(event->mask & IN_CREATE))
+                       continue;
+               netnspath = ns_netns_pathname(NULL, event->name);
+               if (!netnspath)
+                       continue;
+               netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath);
+               netnsinfo = XCALLOC(MTYPE_NETNS_MISC,
+                                   sizeof(struct zebra_netns_info));
+               netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES;
+               netnsinfo->netnspath = netnspath;
+               thread_add_timer_msec(zebrad.master, zebra_ns_ready_read,
+                                      (void *)netnsinfo, 0, NULL);
+       }
+       return 0;
+}
+
+void zebra_ns_notify_parse(void)
+{
+       struct dirent *dent;
+       DIR *srcdir = opendir(NS_RUN_DIR);
+
+       if (srcdir == NULL) {
+               zlog_warn("NS parsing init: failed to parse %s", NS_RUN_DIR);
+               return;
+       }
+       while ((dent = readdir(srcdir)) != NULL) {
+               struct stat st;
+
+               if (strcmp(dent->d_name, ".") == 0
+                  || strcmp(dent->d_name, "..") == 0)
+                       continue;
+               if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
+                       zlog_warn("NS parsing init: failed to parse entry %s",
+                                 dent->d_name);
+                       continue;
+               }
+               if (S_ISDIR(st.st_mode)) {
+                       zlog_warn("NS parsing init: %s is not a NS",
+                                 dent->d_name);
+                       continue;
+               }
+               zebra_ns_notify_create_context_from_entry_name(dent->d_name);
+       }
+       closedir(srcdir);
+}
+
+void zebra_ns_notify_init(void)
+{
+       int fd_monitor;
+
+       zebra_netns_notify_current = NULL;
+       fd_monitor = inotify_init();
+       if (fd_monitor < 0) {
+               zlog_warn("NS notify init: failed to initialize inotify (%s)",
+                         safe_strerror(errno));
+       }
+       if (inotify_add_watch(fd_monitor, NS_RUN_DIR, IN_CREATE) < 0) {
+               zlog_warn("NS notify watch: failed to add watch (%s)",
+                         safe_strerror(errno));
+       }
+       zebra_netns_notify_current = thread_add_read(zebrad.master,
+                                                    zebra_ns_notify_read,
+                                                    NULL, fd_monitor, NULL);
+}
+
+void zebra_ns_notify_close(void)
+{
+       if (zebra_netns_notify_current == NULL)
+               return;
+
+       int fd = 0;
+
+       if (zebra_netns_notify_current->u.fd > 0)
+               fd = zebra_netns_notify_current->u.fd;
+       thread_cancel(zebra_netns_notify_current);
+       /* auto-removal of inotify items */
+       if (fd > 0)
+               close(fd);
+}
+
+#else
+void zebra_ns_notify_parse(void)
+{
+}
+
+void zebra_ns_notify_init(void)
+{
+}
+
+void zebra_ns_notify_close(void)
+{
+}
+#endif /* !HAVE_NETLINK */
diff --git a/zebra/zebra_netns_notify.h b/zebra/zebra_netns_notify.h
new file mode 100644 (file)
index 0000000..0ced749
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Zebra NS collector and notifier for Network NameSpaces
+ * Copyright (C) 2017 6WIND
+ *
+ * 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 _NETNS_NOTIFY_H
+#define _NETNS_NOTIFY_H
+
+extern void zebra_ns_notify_init(void);
+extern void zebra_ns_notify_parse(void);
+extern void zebra_ns_notify_close(void);
+
+extern struct zebra_privs_t zserv_privs;
+
+#endif /* NETNS_NOTIFY_H */
index c48a6f7bd8ad377e8b7e882281a5f0f8cc147ea1..cb302985c8aa6e71128370342cbb6bee96795554 100644 (file)
@@ -1,6 +1,7 @@
 /* zebra NS Routines
  * Copyright (C) 2016 Cumulus Networks, Inc.
  *                    Donald Sharp
+ * Copyright (C) 2017/2018 6WIND
  *
  * This file is part of Quagga.
  *
@@ -22,6 +23,7 @@
 
 #include "lib/ns.h"
 #include "lib/vrf.h"
+#include "lib/logicalrouter.h"
 #include "lib/prefix.h"
 #include "lib/memory.h"
 
 #include "zebra_memory.h"
 #include "rt.h"
 #include "zebra_vxlan.h"
+#include "debug.h"
+#include "zebra_netns_notify.h"
+#include "zebra_netns_id.h"
+
+extern struct zebra_privs_t zserv_privs;
 
 DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space")
 
+static inline int
+zebra_ns_table_entry_compare(const struct zebra_ns_table *e1,
+                            const struct zebra_ns_table *e2);
+
+RB_GENERATE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry,
+           zebra_ns_table_entry_compare);
+
 static struct zebra_ns *dzns;
 
+static inline int
+zebra_ns_table_entry_compare(const struct zebra_ns_table *e1,
+                            const struct zebra_ns_table *e2)
+{
+       if (e1->tableid == e2->tableid)
+               return (e1->afi - e2->afi);
+
+       return e1->tableid - e2->tableid;
+}
+
+static int logicalrouter_config_write(struct vty *vty);
+
 struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id)
 {
-       return dzns;
+       if (ns_id == NS_DEFAULT)
+               return dzns;
+       struct zebra_ns *info = (struct zebra_ns *)ns_info_lookup(ns_id);
+
+       return (info == NULL) ? dzns : info;
+}
+
+static struct zebra_ns *zebra_ns_alloc(void)
+{
+       return XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns));
+}
+
+static int zebra_ns_new(struct ns *ns)
+{
+       struct zebra_ns *zns;
+
+       if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id);
+
+       zns = zebra_ns_alloc();
+       ns->info = zns;
+       zns->ns = ns;
+
+       /* Do any needed per-NS data structure allocation. */
+       zns->if_table = route_table_init();
+       zebra_vxlan_ns_init(zns);
+
+       return 0;
+}
+
+static int zebra_ns_delete(struct ns *ns)
+{
+       struct zebra_ns *zns = (struct zebra_ns *) ns->info;
+
+       if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id);
+       if (!zns)
+               return 0;
+       XFREE(MTYPE_ZEBRA_NS, zns);
+       return 0;
+}
+
+static int zebra_ns_enabled(struct ns *ns)
+{
+       struct zebra_ns *zns = ns->info;
+
+       if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_info("ZNS %s with id %u (enabled)", ns->name, ns->ns_id);
+       if (!zns)
+               return 0;
+       return zebra_ns_enable(ns->ns_id, (void **)&zns);
+}
+
+int zebra_ns_disabled(struct ns *ns)
+{
+       struct zebra_ns *zns = ns->info;
+
+       if (IS_ZEBRA_DEBUG_EVENT)
+               zlog_info("ZNS %s with id %u (disabled)", ns->name, ns->ns_id);
+       if (!zns)
+               return 0;
+       return zebra_ns_disable(ns->ns_id, (void **)&zns);
 }
 
 /* Do global enable actions - open sockets, read kernel config etc. */
@@ -46,6 +133,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
 {
        struct zebra_ns *zns = (struct zebra_ns *)(*info);
 
+       zns->ns_id = ns_id;
+
 #if defined(HAVE_RTADV)
        rtadv_init(zns);
 #endif
@@ -57,10 +146,79 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
        return 0;
 }
 
+struct route_table *zebra_ns_find_table(struct zebra_ns *zns,
+                                       uint32_t tableid, afi_t afi)
+{
+       struct zebra_ns_table finder;
+       struct zebra_ns_table *znst;
+
+       memset(&finder, 0, sizeof(finder));
+       finder.afi = afi;
+       finder.tableid = tableid;
+       znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder);
+
+       if (znst)
+               return znst->table;
+       else
+               return NULL;
+}
+
+struct route_table *zebra_ns_get_table(struct zebra_ns *zns,
+                                      struct zebra_vrf *zvrf, uint32_t tableid,
+                                      afi_t afi)
+{
+       struct zebra_ns_table finder;
+       struct zebra_ns_table *znst;
+       rib_table_info_t *info;
+
+       memset(&finder, 0, sizeof(finder));
+       finder.afi = afi;
+       finder.tableid = tableid;
+       znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder);
+
+       if (znst)
+               return znst->table;
+
+       znst = XCALLOC(MTYPE_ZEBRA_NS, sizeof(*znst));
+       znst->tableid = tableid;
+       znst->afi = afi;
+       znst->table =
+               (afi == AFI_IP6) ? srcdest_table_init() : route_table_init();
+
+       info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info));
+       info->zvrf = zvrf;
+       info->afi = afi;
+       info->safi = SAFI_UNICAST;
+       znst->table->info = info;
+       znst->table->cleanup = zebra_rtable_node_cleanup;
+
+       RB_INSERT(zebra_ns_table_head, &zns->ns_tables, znst);
+       return znst->table;
+}
+
+static void zebra_ns_free_table(struct zebra_ns_table *znst)
+{
+       void *table_info;
+
+       rib_close_table(znst->table);
+
+       table_info = znst->table->info;
+       route_table_finish(znst->table);
+       XFREE(MTYPE_RIB_TABLE_INFO, table_info);
+       XFREE(MTYPE_ZEBRA_NS, znst);
+}
+
 int zebra_ns_disable(ns_id_t ns_id, void **info)
 {
+       struct zebra_ns_table *znst;
        struct zebra_ns *zns = (struct zebra_ns *)(*info);
 
+       while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) {
+               znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables);
+
+               RB_REMOVE(zebra_ns_table_head, &zns->ns_tables, znst);
+               zebra_ns_free_table(znst);
+       }
        route_table_finish(zns->if_table);
        zebra_vxlan_ns_disable(zns);
 #if defined(HAVE_RTADV)
@@ -69,14 +227,27 @@ int zebra_ns_disable(ns_id_t ns_id, void **info)
 
        kernel_terminate(zns);
 
+       zns->ns_id = NS_DEFAULT;
+
        return 0;
 }
 
+
 int zebra_ns_init(void)
 {
-       dzns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns));
+       ns_id_t ns_id;
+
+       dzns = zebra_ns_alloc();
+
+       if (zserv_privs.change(ZPRIVS_RAISE))
+               zlog_err("Can't raise privileges");
+       ns_id = zebra_ns_id_get_default();
+       if (zserv_privs.change(ZPRIVS_LOWER))
+               zlog_err("Can't lower privileges");
+
+       ns_init_management(ns_id);
 
-       ns_init();
+       logicalrouter_init(logicalrouter_config_write);
 
        /* Do any needed per-NS data structure allocation. */
        dzns->if_table = route_table_init();
@@ -86,7 +257,37 @@ int zebra_ns_init(void)
        zebra_vrf_init();
 
        /* Default NS is activated */
-       zebra_ns_enable(NS_DEFAULT, (void **)&dzns);
+       zebra_ns_enable(ns_id, (void **)&dzns);
 
+       if (vrf_is_backend_netns()) {
+               ns_add_hook(NS_NEW_HOOK, zebra_ns_new);
+               ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled);
+               ns_add_hook(NS_DISABLE_HOOK, zebra_ns_disabled);
+               ns_add_hook(NS_DELETE_HOOK, zebra_ns_delete);
+               zebra_ns_notify_parse();
+               zebra_ns_notify_init();
+       }
+       return 0;
+}
+
+static int logicalrouter_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;
+}
+
+int zebra_ns_config_write(struct vty *vty, struct ns *ns)
+{
+       if (ns && ns->name != NULL)
+               vty_out(vty, " netns %s\n", ns->name);
        return 0;
 }
index 5d90b9be67d3737ffed0c8f8797de72ee2455de6..3a998a49ff54b5a26e1d2670c6a8eb524944cde2 100644 (file)
@@ -34,6 +34,18 @@ struct nlsock {
 };
 #endif
 
+struct zebra_ns_table {
+       RB_ENTRY(zebra_ns_table) zebra_ns_table_entry;
+
+       uint32_t tableid;
+       afi_t afi;
+
+       struct route_table *table;
+};
+RB_HEAD(zebra_ns_table_head, zebra_ns_table);
+RB_PROTOTYPE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry,
+            zebra_ns_table_entry_compare)
+
 struct zebra_ns {
        /* net-ns name.  */
        char name[VRF_NAMSIZ];
@@ -55,11 +67,24 @@ struct zebra_ns {
 #if defined(HAVE_RTADV)
        struct rtadv rtadv;
 #endif /* HAVE_RTADV */
+
+       struct zebra_ns_table_head ns_tables;
+
+       /* Back pointer */
+       struct ns *ns;
 };
 
 struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);
 
 int zebra_ns_init(void);
 int zebra_ns_enable(ns_id_t ns_id, void **info);
+int zebra_ns_disabled(struct ns *ns);
 int zebra_ns_disable(ns_id_t ns_id, void **info);
+
+extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns,
+                                              uint32_t tableid, afi_t afi);
+extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns,
+                                             struct zebra_vrf *zvrf,
+                                             uint32_t tableid, afi_t afi);
+int zebra_ns_config_write(struct vty *vty, struct ns *ns);
 #endif
index bbd01a759e9e71358fb78ccd842b76a496894b6e..96bee36be6eaba3de0e7c8b211f79b10b5ed8ebe 100644 (file)
@@ -294,8 +294,11 @@ void zebra_pw_exit(struct zebra_vrf *zvrf)
 {
        struct zebra_pw *pw;
 
-       while ((pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires)) != NULL)
+       while (!RB_EMPTY(zebra_pw_head, &zvrf->pseudowires)) {
+               pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires);
+
                zebra_pw_del(zvrf, pw);
+       }
 }
 
 DEFUN_NOSH (pseudowire_if,
index 967bc92850cb9d5282adf670fd15dc26ac77f009..5c316e077fbd4f924bf03e2b7c5129b256f2f48b 100644 (file)
@@ -416,8 +416,6 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
 
        if (set) {
                UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
-               zebra_deregister_rnh_static_nexthops(nexthop->vrf_id,
-                                                    nexthop->resolved, top);
                nexthops_free(nexthop->resolved);
                nexthop->resolved = NULL;
                re->nexthop_mtu = 0;
@@ -1033,8 +1031,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p,
                        else
                                UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
                }
-               zsend_route_notify_owner(re->type, re->instance, re->vrf_id,
-                                        p, ZAPI_ROUTE_INSTALLED);
+               zsend_route_notify_owner(re, p, ZAPI_ROUTE_INSTALLED);
                break;
        case SOUTHBOUND_INSTALL_FAILURE:
                /*
@@ -1044,8 +1041,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p,
                 */
                dest->selected_fib = re;
 
-               zsend_route_notify_owner(re->type, re->instance, re->vrf_id,
-                                        p, ZAPI_ROUTE_FAIL_INSTALL);
+               zsend_route_notify_owner(re, p, ZAPI_ROUTE_FAIL_INSTALL);
                zlog_warn("%u:%s: Route install failed", re->vrf_id,
                          prefix2str(p, buf, sizeof(buf)));
                break;
@@ -1112,10 +1108,8 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
         * If this is a replace to a new RE let the originator of the RE
         * know that they've lost
         */
-       if (old && old != re)
-               zsend_route_notify_owner(old->type, old->instance,
-                                        old->vrf_id, p,
-                                        ZAPI_ROUTE_BETTER_ADMIN_WON);
+       if (old && (old != re) && (old->type != re->type))
+               zsend_route_notify_owner(old, p, ZAPI_ROUTE_BETTER_ADMIN_WON);
 
        /*
         * Make sure we update the FPM any time we send new information to
@@ -1150,7 +1144,8 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
         */
        hook_call(rib_update, rn, "uninstalling from kernel");
        kernel_route_rib(rn, p, src_p, re, NULL);
-       zvrf->removals++;
+       if (zvrf)
+               zvrf->removals++;
 
        return;
 }
@@ -2115,7 +2110,9 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
                dest->selected_fib = NULL;
 
        /* free RE and nexthops */
-       zebra_deregister_rnh_static_nexthops(re->vrf_id, re->nexthop, rn);
+       if (re->type == ZEBRA_ROUTE_STATIC)
+               zebra_deregister_rnh_static_nexthops(re->vrf_id,
+                                                    re->nexthop, rn);
        nexthops_free(re->nexthop);
        XFREE(MTYPE_RE, re);
 }
index b9b3048486f7132b94d5dade28790c2a3624c77c..bb15fd04f34bd6aa97a9830900c93b9019503290 100644 (file)
@@ -25,8 +25,9 @@
 #include "command.h"
 #include "memory.h"
 #include "srcdest_table.h"
-
+#include "vrf.h"
 #include "vty.h"
+
 #include "zebra/debug.h"
 #include "zebra/zserv.h"
 #include "zebra/rib.h"
@@ -76,7 +77,7 @@ void zebra_vrf_update_all(struct zserv *client)
        struct vrf *vrf;
 
        RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
-               if (vrf->vrf_id)
+               if (vrf->vrf_id != VRF_UNKNOWN)
                        zsend_vrf_add(client, vrf_info_lookup(vrf->vrf_id));
        }
 }
@@ -90,12 +91,9 @@ static int zebra_vrf_new(struct vrf *vrf)
                zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id);
 
        zvrf = zebra_vrf_alloc();
-       zvrf->zns = zebra_ns_lookup(
-               NS_DEFAULT); /* Point to the global (single) NS */
-       router_id_init(zvrf);
        vrf->info = zvrf;
        zvrf->vrf = vrf;
-
+       router_id_init(zvrf);
        return 0;
 }
 
@@ -116,6 +114,10 @@ static int zebra_vrf_enable(struct vrf *vrf)
                zlog_debug("VRF %s id %u is now active",
                           zvrf_name(zvrf), zvrf_id(zvrf));
 
+       if (vrf_is_backend_netns())
+               zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id);
+       else
+               zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
        /* Inform clients that the VRF is now active. This is an
         * add for the clients.
         */
@@ -174,7 +176,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
        struct static_route *si;
        struct route_table *table;
        struct interface *ifp;
-       u_int32_t table_id;
        afi_t afi;
        safi_t safi;
        unsigned i;
@@ -213,12 +214,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
        for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
                for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
                        rib_close_table(zvrf->table[afi][safi]);
-
-               if (vrf->vrf_id == VRF_DEFAULT)
-                       for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
-                            table_id++)
-                               if (zvrf->other_table[afi][table_id])
-                                       rib_close_table(zvrf->other_table[afi][table_id]);
        }
 
        /* Cleanup Vxlan, MPLS and PW tables. */
@@ -236,7 +231,8 @@ static int zebra_vrf_disable(struct vrf *vrf)
                struct route_node *rnode;
                rib_dest_t *dest;
 
-               for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode, rnode)) {
+               for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i],
+                                      lnode, nnode, rnode)) {
                        dest = rib_dest_from_rnode(rnode);
                        if (dest && rib_dest_vrf(dest) == zvrf) {
                                route_unlock_node(rnode);
@@ -258,17 +254,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
                        zvrf->table[afi][safi] = NULL;
                }
 
-               if (vrf->vrf_id == VRF_DEFAULT)
-                       for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
-                            table_id++)
-                               if (zvrf->other_table[afi][table_id]) {
-                                       table = zvrf->other_table[afi][table_id];
-                                       table_info = table->info;
-                                       route_table_finish(table);
-                                       XFREE(MTYPE_RIB_TABLE_INFO, table_info);
-                                       zvrf->other_table[afi][table_id] = NULL;
-                               }
-
                route_table_finish(zvrf->rnh_table[afi]);
                zvrf->rnh_table[afi] = NULL;
                route_table_finish(zvrf->import_check_table[afi]);
@@ -282,7 +267,6 @@ static int zebra_vrf_delete(struct vrf *vrf)
 {
        struct zebra_vrf *zvrf = vrf->info;
        struct route_table *table;
-       u_int32_t table_id;
        afi_t afi;
        safi_t safi;
        unsigned i;
@@ -328,14 +312,6 @@ static int zebra_vrf_delete(struct vrf *vrf)
                        route_table_finish(table);
                }
 
-               for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; table_id++)
-                       if (zvrf->other_table[afi][table_id]) {
-                               table = zvrf->other_table[afi][table_id];
-                               table_info = table->info;
-                               route_table_finish(table);
-                               XFREE(MTYPE_RIB_TABLE_INFO, table_info);
-                       }
-
                route_table_finish(zvrf->rnh_table[afi]);
                route_table_finish(zvrf->import_check_table[afi]);
        }
@@ -407,8 +383,8 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
        return table;
 }
 
-static void zebra_rtable_node_cleanup(struct route_table *table,
-                                     struct route_node *node)
+void zebra_rtable_node_cleanup(struct route_table *table,
+                              struct route_node *node)
 {
        struct route_entry *re, *next;
 
@@ -545,34 +521,20 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id,
                                                vrf_id_t vrf_id)
 {
        struct zebra_vrf *zvrf;
-       rib_table_info_t *info;
-       struct route_table *table;
+       struct zebra_ns *zns;
 
        zvrf = vrf_info_lookup(vrf_id);
        if (!zvrf)
                return NULL;
 
-       if (afi >= AFI_MAX)
-               return NULL;
+       zns = zvrf->zns;
 
-       if (table_id >= ZEBRA_KERNEL_TABLE_MAX)
+       if (afi >= AFI_MAX)
                return NULL;
 
        if ((vrf_id == VRF_DEFAULT) && (table_id != RT_TABLE_MAIN)
            && (table_id != zebrad.rtm_table_default)) {
-               if (zvrf->other_table[afi][table_id] == NULL) {
-                       table = (afi == AFI_IP6) ? srcdest_table_init()
-                                                : route_table_init();
-                       info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info));
-                       info->zvrf = zvrf;
-                       info->afi = afi;
-                       info->safi = SAFI_UNICAST;
-                       table->info = info;
-                       table->cleanup = zebra_rtable_node_cleanup;
-                       zvrf->other_table[afi][table_id] = table;
-               }
-
-               return (zvrf->other_table[afi][table_id]);
+               return zebra_ns_get_table(zns, zvrf, table_id, afi);
        }
 
        return zvrf->table[afi][SAFI_UNICAST];
@@ -598,7 +560,11 @@ static int vrf_config_write(struct vty *vty)
                if (vrf_is_user_cfged(vrf)) {
                        vty_out(vty, "vrf %s\n", zvrf_name(zvrf));
                        if (zvrf->l3vni)
-                               vty_out(vty, " vni %u\n", zvrf->l3vni);
+                               vty_out(vty, " vni %u%s\n",
+                                       zvrf->l3vni,
+                                       is_l3vni_for_prefix_routes_only(zvrf->l3vni) ?
+                                       " prefix-routes-only" :"");
+                       zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt);
                        vty_out(vty, "!\n");
                }
 
@@ -615,8 +581,8 @@ static int vrf_config_write(struct vty *vty)
 /* Zebra VRF initialization. */
 void zebra_vrf_init(void)
 {
-       vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable,
-                zebra_vrf_delete);
+       vrf_init(zebra_vrf_new, zebra_vrf_enable,
+                zebra_vrf_disable, zebra_vrf_delete);
 
        vrf_cmd_init(vrf_config_write);
 }
index d3a5316b9d4571b53b74c1349cedf43d0d829266..4c12d7dee904bfa55878bf86636545a5c8efcb9f 100644 (file)
@@ -19,8 +19,8 @@
  * 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(__ZEBRA_RIB_H__)
-#define __ZEBRA_RIB_H__
+#if !defined(__ZEBRA_VRF_H__)
+#define __ZEBRA_VRF_H__
 
 #include <zebra/zebra_ns.h>
 #include <zebra/zebra_pw.h>
@@ -62,9 +62,6 @@ struct zebra_vrf {
        /* Import check table (used mostly by BGP */
        struct route_table *import_check_table[AFI_MAX];
 
-       /* Routing tables off of main table for redistribute table */
-       struct route_table *other_table[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX];
-
        /* 2nd pointer type used primarily to quell a warning on
         * ALL_LIST_ELEMENTS_RO
         */
@@ -131,14 +128,28 @@ struct zebra_vrf {
 
 static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf)
 {
+       if (!zvrf || !zvrf->vrf)
+               return VRF_UNKNOWN;
        return zvrf->vrf->vrf_id;
 }
 
+static inline const char *zvrf_ns_name(struct zebra_vrf *zvrf)
+{
+       if (!zvrf->vrf || !zvrf->vrf->ns_ctxt)
+               return NULL;
+       return ns_get_name((struct ns *)zvrf->vrf->ns_ctxt);
+}
+
 static inline const char *zvrf_name(struct zebra_vrf *zvrf)
 {
        return zvrf->vrf->name;
 }
 
+static inline bool zvrf_is_active(struct zebra_vrf *zvrf)
+{
+       return zvrf->vrf->status & VRF_ACTIVE;
+}
+
 struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
                                                  vrf_id_t vrf_id,
                                                  u_int32_t table_id);
@@ -154,4 +165,7 @@ extern struct route_table *
 zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, vrf_id_t vrf_id);
 extern int zebra_vrf_has_config(struct zebra_vrf *zvrf);
 extern void zebra_vrf_init(void);
-#endif
+
+extern void zebra_rtable_node_cleanup(struct route_table *table,
+                                     struct route_node *node);
+#endif /* ZEBRA_VRF_H */
index 269244f7688dec1f72bd89407b46503c1f8cc41a..4824c09f3db89a74581338da5bceac1e342fee24 100644 (file)
@@ -1235,46 +1235,21 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
        }
 }
 
-static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
-                           safi_t safi, bool use_fib, u_char use_json,
-                           route_tag_t tag,
-                           const struct prefix *longer_prefix_p,
-                           bool supernets_only, int type,
-                           u_short ospf_instance_id)
+static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
+                                struct route_table *table, afi_t afi,
+                                bool use_fib, route_tag_t tag,
+                                const struct prefix *longer_prefix_p,
+                                bool supernets_only, int type,
+                                u_short ospf_instance_id, u_char use_json)
 {
-       struct route_table *table;
-       rib_dest_t *dest;
        struct route_node *rn;
        struct route_entry *re;
        int first = 1;
-       struct zebra_vrf *zvrf = NULL;
-       char buf[BUFSIZ];
+       rib_dest_t *dest;
        json_object *json = NULL;
        json_object *json_prefix = NULL;
-       u_int32_t addr;
-
-       if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty, "vrf %s not defined\n", vrf_name);
-               return CMD_SUCCESS;
-       }
-
-       if (zvrf_id(zvrf) == VRF_UNKNOWN) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty, "vrf %s inactive\n", vrf_name);
-               return CMD_SUCCESS;
-       }
-
-       table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
-       if (!table) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               return CMD_SUCCESS;
-       }
+       uint32_t addr;
+       char buf[BUFSIZ];
 
        if (use_json)
                json = json_object_new_object();
@@ -1352,6 +1327,67 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
                                             json, JSON_C_TO_STRING_PRETTY));
                json_object_free(json);
        }
+}
+
+static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
+                           safi_t safi, bool use_fib, u_char use_json,
+                           route_tag_t tag,
+                           const struct prefix *longer_prefix_p,
+                           bool supernets_only, int type,
+                           u_short ospf_instance_id)
+{
+       struct route_table *table;
+       struct zebra_vrf *zvrf = NULL;
+
+       if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty, "vrf %s not defined\n", vrf_name);
+               return CMD_SUCCESS;
+       }
+
+       if (zvrf_id(zvrf) == VRF_UNKNOWN) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty, "vrf %s inactive\n", vrf_name);
+               return CMD_SUCCESS;
+       }
+
+       table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
+       if (!table) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               return CMD_SUCCESS;
+       }
+
+       do_show_route_helper(vty, zvrf, table, afi, use_fib, tag,
+                            longer_prefix_p, supernets_only, type,
+                            ospf_instance_id, use_json);
+
+       return CMD_SUCCESS;
+}
+
+DEFPY (show_route_table,
+       show_route_table_cmd,
+       "show <ip$ipv4|ipv6$ipv6> route table (1-4294967295)$table [json$json]",
+       SHOW_STR
+       IP_STR
+       IP6_STR
+       "IP routing table\n"
+       "Table to display\n"
+       "The table number to display, if available\n"
+       JSON_STR)
+{
+       afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
+       struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+       struct route_table *t;
+
+       t = zebra_ns_find_table(zvrf->zns, table, afi);
+       if (t)
+               do_show_route_helper(vty, zvrf, t, afi, false, 0, false, false,
+                                    0, 0, !!json);
 
        return CMD_SUCCESS;
 }
@@ -2338,8 +2374,12 @@ DEFUN (show_vrf,
                        continue;
 
                vty_out(vty, "vrf %s ", zvrf_name(zvrf));
-               if (zvrf_id(zvrf) == VRF_UNKNOWN)
+               if (zvrf_id(zvrf) == VRF_UNKNOWN
+                   || !zvrf_is_active(zvrf))
                        vty_out(vty, "inactive");
+               else if (zvrf_ns_name(zvrf))
+                       vty_out(vty, "id %u netns %s",
+                               zvrf_id(zvrf), zvrf_ns_name(zvrf));
                else
                        vty_out(vty, "id %u table %u", zvrf_id(zvrf),
                                zvrf->table_id);
@@ -2353,20 +2393,26 @@ DEFUN (show_vrf,
 
 DEFUN (default_vrf_vni_mapping,
        default_vrf_vni_mapping_cmd,
-       "vni " CMD_VNI_RANGE,
+       "vni " CMD_VNI_RANGE "[prefix-routes-only]",
        "VNI corresponding to the DEFAULT VRF\n"
-       "VNI-ID\n")
+       "VNI-ID\n"
+       "Prefix routes only \n")
 {
        int ret = 0;
        char err[ERR_STR_SZ];
        struct zebra_vrf *zvrf = NULL;
        vni_t vni = strtoul(argv[1]->arg, NULL, 10);
+       int filter = 0;
 
        zvrf = vrf_info_lookup(VRF_DEFAULT);
        if (!zvrf)
                return CMD_WARNING;
 
-       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
+       if (argc == 3)
+               filter = 1;
+
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+                                             filter, 1);
        if (ret != 0) {
                vty_out(vty, "%s\n", err);
                return CMD_WARNING;
@@ -2391,7 +2437,7 @@ DEFUN (no_default_vrf_vni_mapping,
        if (!zvrf)
                return CMD_WARNING;
 
-       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
        if (ret != 0) {
                vty_out(vty, "%s\n", err);
                return CMD_WARNING;
@@ -2402,11 +2448,13 @@ DEFUN (no_default_vrf_vni_mapping,
 
 DEFUN (vrf_vni_mapping,
        vrf_vni_mapping_cmd,
-       "vni " CMD_VNI_RANGE,
+       "vni " CMD_VNI_RANGE "[prefix-routes-only]",
        "VNI corresponding to tenant VRF\n"
-       "VNI-ID\n")
+       "VNI-ID\n"
+       "prefix-routes-only\n")
 {
        int ret = 0;
+       int filter = 0;
 
        ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
        vni_t vni = strtoul(argv[1]->arg, NULL, 10);
@@ -2415,9 +2463,13 @@ DEFUN (vrf_vni_mapping,
        assert(vrf);
        assert(zvrf);
 
+       if (argc == 3)
+               filter = 1;
+
        /* Mark as having FRR configuration */
        vrf_set_user_cfged(vrf);
-       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+                                             filter, 1);
        if (ret != 0) {
                vty_out(vty, "%s\n", err);
                return CMD_WARNING;
@@ -2442,7 +2494,7 @@ DEFUN (no_vrf_vni_mapping,
        assert(vrf);
        assert(zvrf);
 
-       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
        if (ret != 0) {
                vty_out(vty, "%s\n", err);
                return CMD_WARNING;
@@ -3341,6 +3393,7 @@ void zebra_vty_init(void)
        install_element(VIEW_NODE, &show_vrf_cmd);
        install_element(VIEW_NODE, &show_vrf_vni_cmd);
        install_element(VIEW_NODE, &show_route_cmd);
+       install_element(VIEW_NODE, &show_route_table_cmd);
        install_element(VIEW_NODE, &show_route_detail_cmd);
        install_element(VIEW_NODE, &show_route_summary_cmd);
        install_element(VIEW_NODE, &show_ip_nht_cmd);
index c9cc556a4478f6ea520d646763c775efb6c17778..20b9e94288792c5d433fce497af9bc7f2157f2b1 100644 (file)
@@ -928,6 +928,9 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
                        zl3vni_svi_if_name(zl3vni));
                vty_out(vty, "  State: %s\n",
                        zl3vni_state2str(zl3vni));
+               vty_out(vty, "  VNI Filter: %s\n",
+                       CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
+                               "prefix-routes-only" : "none");
                vty_out(vty, "  Router MAC: %s\n",
                        zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
                vty_out(vty, "  L2 VNIs: ");
@@ -951,6 +954,10 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
                json_object_string_add(json, "routerMac",
                                       zl3vni_rmac2str(zl3vni, buf,
                                                       sizeof(buf)));
+               json_object_string_add(json, "vniFilter",
+                                      CHECK_FLAG(zl3vni->filter,
+                                                 PREFIX_ROUTES_ONLY) ?
+                                               "prefix-routes-only" : "none");
                for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
                        json_object_array_add(json_vni_list,
                                              json_object_new_int(zvni->vni));
@@ -3613,15 +3620,19 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
        stream_putl(s, zl3vni->vni);
        stream_put(s, &rmac, sizeof(struct ethaddr));
        stream_put_in_addr(s, &zl3vni->local_vtep_ip);
+       stream_put(s, &zl3vni->filter, sizeof(int));
 
        /* Write packet size. */
        stream_putw_at(s, 0, stream_get_endp(s));
 
        if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s",
+               zlog_debug(
+                          "Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s filter %s to %s",
                           zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
                           prefix_mac2str(&rmac, buf, sizeof(buf)),
                           inet_ntoa(zl3vni->local_vtep_ip),
+                          CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
+                               "prefix-routes-only" : "none",
                           zebra_route_string(client->proto));
 
        client->l3vniadd_cnt++;
@@ -3666,10 +3677,6 @@ static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
        if (!zl3vni)
                return;
 
-       if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("L3-VNI %u is UP - send add to BGP",
-                          zl3vni->vni);
-
        /* send l3vni add to BGP */
        zl3vni_send_add_to_client(zl3vni);
 }
@@ -3679,10 +3686,6 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
        if (!zl3vni)
                return;
 
-       if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("L3-VNI %u is Down - Send del to BGP",
-                          zl3vni->vni);
-
        /* send l3-vni del to BGP*/
        zl3vni_send_del_to_client(zl3vni);
 }
@@ -3835,6 +3838,17 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
 
 /* Public functions */
 
+int is_l3vni_for_prefix_routes_only(vni_t vni)
+{
+       zebra_l3vni_t *zl3vni = NULL;
+
+       zl3vni = zl3vni_lookup(vni);
+       if (!zl3vni)
+               return 0;
+
+       return CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? 1 : 0;
+}
+
 /* handle evpn route in vrf table */
 void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
                                   struct ethaddr *rmac,
@@ -6432,7 +6446,7 @@ int zebra_vxlan_if_add(struct interface *ifp)
 int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
                                    vni_t vni,
                                    char *err, int err_str_sz,
-                                   int add)
+                                   int filter, int add)
 {
        zebra_l3vni_t *zl3vni = NULL;
        struct zebra_vrf *zvrf_default = NULL;
@@ -6477,6 +6491,12 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
                /* associate the vrf with vni */
                zvrf->l3vni = vni;
 
+               /* set the filter in l3vni to denote if we are using l3vni only
+                * for prefix routes
+                */
+               if (filter)
+                       SET_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY);
+
                /* associate with vxlan-intf;
                 * we need to associate with the vxlan-intf first
                 */
index d9801a8b6059d4bcbeec9f569aa249a8781653e2..df9b07db60f90065a35ef94df68dc8ce326ffe08 100644 (file)
@@ -52,6 +52,7 @@ is_evpn_enabled()
 
 #define VNI_STR_LEN 32
 
+extern int is_l3vni_for_prefix_routes_only(vni_t vni);
 extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id);
 extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf);
 extern int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf);
@@ -154,7 +155,7 @@ extern int zebra_vxlan_advertise_all_vni(struct zserv *client,
                                         struct zebra_vrf *zvrf);
 extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
                                           char *err,
-                                          int err_str_sz, int add);
+                                          int err_str_sz, int filter, int add);
 extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf);
 extern void zebra_vxlan_close_tables(struct zebra_vrf *);
 extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *);
index 8d34b3e2f12bbc613b51ec50231fc1b8f4322af8..e8de25cefd722bc61526abe3bafef27f9449eaaa 100644 (file)
@@ -101,6 +101,9 @@ struct zebra_l3vni_t_ {
        /* vrf_id */
        vrf_id_t vrf_id;
 
+       uint32_t filter;
+#define PREFIX_ROUTES_ONLY     (1 << 0) /* l3-vni used for prefix routes only */
+
        /* Local IP */
        struct in_addr local_vtep_ip;
 
index b3b1fa79e9ba89880ba1bcecf965cdbbad1fa398..98cb54f7b8f9a827fd52f38d35300be094faa43e 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <zebra.h>
 #include <sys/un.h>
+/* for basename */
+#include <libgen.h>
 
 #include "prefix.h"
 #include "command.h"
@@ -182,13 +184,19 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp)
 static void zserv_encode_vrf(struct stream *s, struct zebra_vrf *zvrf)
 {
        struct vrf_data data;
+       const char *netns_name = zvrf_ns_name(zvrf);
 
        data.l.table_id = zvrf->table_id;
-       /* Pass the tableid */
+
+       if (netns_name)
+               strlcpy(data.l.netns_name,
+                       basename((char *)netns_name), NS_NAMSIZ);
+       else
+               memset(data.l.netns_name, 0, NS_NAMSIZ);
+       /* Pass the tableid and the netns NAME */
        stream_put(s, &data, sizeof(struct vrf_data));
        /* Interface information. */
        stream_put(s, zvrf_name(zvrf), VRF_NAMSIZ);
-
        /* Write packet size. */
        stream_putw_at(s, 0, stream_get_endp(s));
 }
@@ -978,29 +986,38 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client,
        return zebra_server_send_message(client);
 }
 
-int zsend_route_notify_owner(u_char proto, u_short instance,
-                            vrf_id_t vrf_id, struct prefix *p,
+int zsend_route_notify_owner(struct route_entry *re, struct prefix *p,
                             enum zapi_route_notify_owner note)
 {
        struct zserv *client;
        struct stream *s;
        uint8_t blen;
 
-       client = zebra_find_client(proto, instance);
+       client = zebra_find_client(re->type, re->instance);
        if (!client || !client->notify_owner) {
                if (IS_ZEBRA_DEBUG_PACKET) {
                        char buff[PREFIX_STRLEN];
 
-                       zlog_debug("Not Notifying Owner: %u about prefix %s",
-                                  proto, prefix2str(p, buff, sizeof(buff)));
+                       zlog_debug(
+                               "Not Notifying Owner: %u about prefix %s(%u) %d",
+                               re->type, prefix2str(p, buff, sizeof(buff)),
+                               re->table, note);
                }
                return 0;
        }
 
+       if (IS_ZEBRA_DEBUG_PACKET) {
+               char buff[PREFIX_STRLEN];
+
+               zlog_debug("Notifying Owner: %u about prefix %s(%u) %d",
+                          re->type, prefix2str(p, buff, sizeof(buff)),
+                          re->table, note);
+       }
+
        s = client->obuf;
        stream_reset(s);
 
-       zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, vrf_id);
+       zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, re->vrf_id);
 
        stream_put(s, &note, sizeof(note));
 
@@ -1010,6 +1027,8 @@ int zsend_route_notify_owner(u_char proto, u_short instance,
        stream_putc(s, p->prefixlen);
        stream_put(s, &p->u.prefix, blen);
 
+       stream_putl(s, re->table);
+
        stream_putw_at(s, 0, stream_get_endp(s));
 
        return zebra_server_send_message(client);
index 7d5f6b45437f203c23c6b3f7d1e8e1e27239cb11..20448162270f3595fae43de53dee951a433426c6 100644 (file)
@@ -175,8 +175,7 @@ extern int zsend_interface_vrf_update(struct zserv *, struct interface *,
 extern int zsend_interface_link_params(struct zserv *, struct interface *);
 extern int zsend_pw_update(struct zserv *, struct zebra_pw *);
 
-extern int zsend_route_notify_owner(u_char proto, u_short instance,
-                                   vrf_id_t vrf_id, struct prefix *p,
+extern int zsend_route_notify_owner(struct route_entry *re, struct prefix *p,
                                    enum zapi_route_notify_owner note);
 
 extern void zserv_nexthop_num_warn(const char *, const struct prefix *,