]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #4025 from AnuradhaKaruppiah/pim-evpn
authorJafar Al-Gharaibeh <Jafaral@users.noreply.github.com>
Mon, 22 Apr 2019 16:44:52 +0000 (11:44 -0500)
committerGitHub <noreply@github.com>
Mon, 22 Apr 2019 16:44:52 +0000 (11:44 -0500)
pim-evpn: Forwarding overlay BUM traffic via multicast VxLAN tunnels in the underlay

1  2 
bgpd/bgp_evpn_private.h
doc/user/pim.rst
pimd/pim_cmd.c

diff --combined bgpd/bgp_evpn_private.h
index bdc2b8d64c3e231d7b23edf5e0fba3a62c6793fa,88b57ff9eacd634634da2b3c8d503034bb485248..a5a091242f1636ca61b95d1ca4be5b1ccc68ac1a
@@@ -87,6 -87,9 +87,9 @@@ struct bgpevpn 
        /* Route type 3 field */
        struct in_addr originator_ip;
  
+       /* PIM-SM MDT group for BUM flooding */
+       struct in_addr mcast_grp;
        /* Import and Export RTs. */
        struct list *import_rtl;
        struct list *export_rtl;
@@@ -237,7 -240,6 +240,7 @@@ static inline void bgpevpn_unlink_from_
        listnode_delete(vpn->bgp_vrf->l2vnis, vpn);
  
        /* remove the backpointer to the vrf instance */
 +      bgp_unlock(vpn->bgp_vrf);
        vpn->bgp_vrf = NULL;
  }
  
@@@ -254,7 -256,7 +257,7 @@@ static inline void bgpevpn_link_to_l3vn
                return;
  
        /* associate the vpn to the bgp_vrf instance */
 -      vpn->bgp_vrf = bgp_vrf;
 +      vpn->bgp_vrf = bgp_lock(bgp_vrf);
        listnode_add_sort(bgp_vrf->l2vnis, vpn);
  
        /* check if we are advertising two labels for this vpn */
@@@ -526,8 -528,9 +529,9 @@@ extern void bgp_evpn_derive_auto_rd(str
  extern void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp);
  extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni);
  extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
-                                   struct in_addr originator_ip,
-                                   vrf_id_t tenant_vrf_id);
+               struct in_addr originator_ip,
+               vrf_id_t tenant_vrf_id,
+               struct in_addr mcast_grp);
  extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
  extern struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi);
  extern struct evpnes *bgp_evpn_es_new(struct bgp *bgp, esi_t *esi,
diff --combined doc/user/pim.rst
index df884a7883002360e78587fd78401d544bdb645b,939de30cb4987bf85e97b0f607489408df005b8a..17bf44fdbac5119656bbfcd255d029f8f9c90eff
@@@ -8,7 -8,8 +8,8 @@@ PIM -- Protocol Independent Multicas
  
  *pimd* supports pim-sm as well as igmp v2 and v3. pim is
  vrf aware and can work within the context of vrf's in order to
- do S,G mrouting.
+ do S,G mrouting.  Additionally PIM can be used in the EVPN underlay
+ network for optimizing forwarding of overlay BUM traffic.
  
  .. _starting-and-stopping-pimd:
  
@@@ -333,12 -334,10 +334,12 @@@ cause great confusion
  
     Display information about interfaces PIM is using.
  
 -.. index:: show ip pim join
 +.. index:: show ip pim [vrf NAME] join [A.B.C.D [A.B.C.D]] [json]
  .. clicmd:: show ip pim join
  
 -   Display information about PIM joins received.
 +   Display information about PIM joins received.  If one address is specified
 +   then we assume it is the Group we are interested in displaying data on.
 +   If the second address is specified then it is Source Group.
  
  .. index:: show ip pim local-membership
  .. clicmd:: show ip pim local-membership
     Display information about known S,G's and incoming interface as well as the
     OIL and how they were chosen.
  
 -.. index:: show ip pim upstream
 +.. index:: show ip pim [vrf NAME] upstream [A.B.C.D [A.B.C.D]] [json]
  .. clicmd:: show ip pim upstream
  
 -   Display upstream information about a S,G mroute.
 +   Display upstream information about a S,G mroute.  Allow the user to
 +   specify sub Source and Groups that we are only interested in.
  
  .. index:: show ip pim upstream-join-desired
  .. clicmd:: show ip pim upstream-join-desired
@@@ -498,3 -496,23 +499,23 @@@ Clear commands reset various variables
  .. clicmd:: clear ip pim oil
  
     Rescan PIM OIL (output interface list).
+ PIM EVPN configuration
+ ======================
+ To use PIM in the underlay for overlay BUM forwarding associate a
+ multicast group with the L2 VNI. The actual configuration is based
+ on your distribution. Here is an ifupdown2 example -
+ auto vx-10100
+ iface vx-10100
+       vxlan-id 10100
+       bridge-access 100
+       vxlan-local-tunnelip 27.0.0.11
+       vxlan-mcastgrp 239.1.1.100 >>>>>>>>
+ PIM will see the vxlan configuration and auto configure state to properly
+ forward BUM traffic.
+ PIM also needs to be configured in the underlay to allow the BUM MDT to
+ be setup. This is existing PIM configuration -
+ - Enable pim on the underlay L3 interface via the "ip pim" command.
+ - Configure RPs for the BUM multicast group range.
+ - Ensure the PIM is enabled on the lo of the VTEPs and the RP.
diff --combined pimd/pim_cmd.c
index 110408001f24b20136267461bcbbfab03c91acb9,736d348292e0c567bc2d4e3968776329b01b234b..e1dfb0006118f62d00e1166ddab635796b85e733
@@@ -60,6 -60,7 +60,7 @@@
  #include "pim_ssm.h"
  #include "pim_nht.h"
  #include "pim_bfd.h"
+ #include "pim_vxlan.h"
  #include "bfd.h"
  
  #ifndef VTYSH_EXTRACT_PL
@@@ -1696,8 -1697,7 +1697,8 @@@ static void pim_show_join_helper(struc
        }
  }
  
 -static void pim_show_join(struct pim_instance *pim, struct vty *vty, bool uj)
 +static void pim_show_join(struct pim_instance *pim, struct vty *vty,
 +                        struct prefix_sg *sg, bool uj)
  {
        struct pim_interface *pim_ifp;
        struct pim_ifchannel *ch;
                        continue;
  
                RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
 +                      if (sg->grp.s_addr != 0
 +                          && sg->grp.s_addr != ch->sg.grp.s_addr)
 +                              continue;
 +                      if (sg->src.s_addr != 0
 +                          && sg->src.s_addr != ch->sg.src.s_addr)
 +                      continue;
                        pim_show_join_helper(vty, pim_ifp, ch, json, now, uj);
                } /* scan interface channels */
        }
@@@ -1963,7 -1957,7 +1964,7 @@@ static void pim_show_state(struct pim_i
                json = json_object_new_object();
        } else {
                vty_out(vty,
-                       "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G)");
+                       "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN");
                vty_out(vty,
                        "\nInstalled Source           Group            IIF               OIL\n");
        }
                        } else {
                                if (first_oif) {
                                        first_oif = 0;
-                                       vty_out(vty, "%s(%c%c%c%c)", out_ifname,
+                                       vty_out(vty, "%s(%c%c%c%c%c)", out_ifname,
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_IGMP)
                                                        ? 'I'
                                                 & PIM_OIF_FLAG_PROTO_PIM)
                                                        ? 'J'
                                                        : ' ',
+                                               (c_oil->oif_flags[oif_vif_index]
+                                                & PIM_OIF_FLAG_PROTO_VXLAN)
+                                                       ? 'V'
+                                                       : ' ',
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_SOURCE)
                                                        ? 'S'
                                                        ? '*'
                                                        : ' ');
                                } else
-                                       vty_out(vty, ", %s(%c%c%c%c)",
+                                       vty_out(vty, ", %s(%c%c%c%c%c)",
                                                out_ifname,
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_IGMP)
                                                 & PIM_OIF_FLAG_PROTO_PIM)
                                                        ? 'J'
                                                        : ' ',
+                                               (c_oil->oif_flags[oif_vif_index]
+                                                & PIM_OIF_FLAG_PROTO_VXLAN)
+                                                       ? 'V'
+                                                       : ' ',
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_SOURCE)
                                                        ? 'S'
@@@ -2342,7 -2344,7 +2351,7 @@@ static const char *pim_reg_state2brief_
  }
  
  static void pim_show_upstream(struct pim_instance *pim, struct vty *vty,
 -                            bool uj)
 +                            struct prefix_sg *sg, bool uj)
  {
        struct listnode *upnode;
        struct pim_upstream *up;
                char msdp_reg_timer[10];
                char state_str[PIM_REG_STATE_STR_LEN];
  
 +              if (sg->grp.s_addr != 0 && sg->grp.s_addr != up->sg.grp.s_addr)
 +                      continue;
 +              if (sg->src.s_addr != 0 && sg->src.s_addr != up->sg.src.s_addr)
 +                      continue;
 +
                pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
                pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
                pim_time_uptime(uptime, sizeof(uptime),
@@@ -3789,45 -3786,24 +3798,45 @@@ DEFUN (show_ip_pim_interface_vrf_all
        return CMD_SUCCESS;
  }
  
 -DEFUN (show_ip_pim_join,
 +DEFPY (show_ip_pim_join,
         show_ip_pim_join_cmd,
 -       "show ip pim [vrf NAME] join [json]",
 +       "show ip pim [vrf NAME] join [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]",
         SHOW_STR
         IP_STR
         PIM_STR
         VRF_CMD_HELP_STR
         "PIM interface join information\n"
 +       "The Source or Group\n"
 +       "The Group\n"
         JSON_STR)
  {
 -      int idx = 2;
 -      struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
 -      bool uj = use_json(argc, argv);
 +      struct prefix_sg sg = {0};
 +      struct vrf *v;
 +      bool uj = !!json;
 +      struct pim_instance *pim;
  
 -      if (!vrf)
 +      v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
 +
 +      if (!v) {
 +              vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf);
                return CMD_WARNING;
 +      }
 +      pim = pim_get_pim_instance(v->vrf_id);
 +
 +      if (!pim) {
 +              vty_out(vty, "%% Unable to find pim instance\n");
 +              return CMD_WARNING;
 +      }
  
 -      pim_show_join(vrf->info, vty, uj);
 +      if (s_or_g.s_addr != 0) {
 +              if (g.s_addr != 0) {
 +                      sg.src = s_or_g;
 +                      sg.grp = g;
 +              } else
 +                      sg.grp = s_or_g;
 +      }
 +
 +      pim_show_join(pim, vty, &sg, uj);
  
        return CMD_SUCCESS;
  }
@@@ -3842,7 -3818,6 +3851,7 @@@ DEFUN (show_ip_pim_join_vrf_all
         "PIM interface join information\n"
         JSON_STR)
  {
 +      struct prefix_sg sg = {0};
        bool uj = use_json(argc, argv);
        struct vrf *vrf;
        bool first = true;
                        first = false;
                } else
                        vty_out(vty, "VRF: %s\n", vrf->name);
 -              pim_show_join(vrf->info, vty, uj);
 +              pim_show_join(vrf->info, vty, &sg, uj);
        }
        if (uj)
                vty_out(vty, "}\n");
@@@ -4056,44 -4031,24 +4065,44 @@@ DEFUN (show_ip_pim_state_vrf_all
        return CMD_SUCCESS;
  }
  
 -DEFUN (show_ip_pim_upstream,
 +DEFPY (show_ip_pim_upstream,
         show_ip_pim_upstream_cmd,
 -       "show ip pim [vrf NAME] upstream [json]",
 +       "show ip pim [vrf NAME] upstream [A.B.C.D$s_or_g [A.B.C.D$g]] [json$json]",
         SHOW_STR
         IP_STR
         PIM_STR
         VRF_CMD_HELP_STR
         "PIM upstream information\n"
 +       "The Source or Group\n"
 +       "The Group\n"
         JSON_STR)
  {
 -      int idx = 2;
 -      struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
 -      bool uj = use_json(argc, argv);
 +      struct prefix_sg sg = {0};
 +      struct vrf *v;
 +      bool uj = !!json;
 +      struct pim_instance *pim;
  
 -      if (!vrf)
 +      v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
 +
 +      if (!v) {
 +              vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf);
                return CMD_WARNING;
 +      }
 +      pim = pim_get_pim_instance(v->vrf_id);
  
 -      pim_show_upstream(vrf->info, vty, uj);
 +      if (!pim) {
 +              vty_out(vty, "%% Unable to find pim instance\n");
 +              return CMD_WARNING;
 +      }
 +
 +      if (s_or_g.s_addr != 0) {
 +              if (g.s_addr != 0) {
 +                      sg.src = s_or_g;
 +                      sg.grp = g;
 +              } else
 +                      sg.grp = s_or_g;
 +      }
 +      pim_show_upstream(pim, vty, &sg, uj);
  
        return CMD_SUCCESS;
  }
@@@ -4108,7 -4063,6 +4117,7 @@@ DEFUN (show_ip_pim_upstream_vrf_all
         "PIM upstream information\n"
         JSON_STR)
  {
 +      struct prefix_sg sg = {0};
        bool uj = use_json(argc, argv);
        struct vrf *vrf;
        bool first = true;
                        first = false;
                } else
                        vty_out(vty, "VRF: %s\n", vrf->name);
 -              pim_show_upstream(vrf->info, vty, uj);
 +              pim_show_upstream(vrf->info, vty, &sg, uj);
        }
  
        return CMD_SUCCESS;
@@@ -4671,6 -4625,11 +4680,11 @@@ static void show_mroute(struct pim_inst
                                        json_object_boolean_true_add(
                                                json_ifp_out, "protocolIgmp");
  
+                               if (c_oil->oif_flags[oif_vif_index]
+                                   & PIM_OIF_FLAG_PROTO_VXLAN)
+                                       json_object_boolean_true_add(
+                                               json_ifp_out, "protocolVxlan");
                                if (c_oil->oif_flags[oif_vif_index]
                                    & PIM_OIF_FLAG_PROTO_SOURCE)
                                        json_object_boolean_true_add(
                                        strcpy(proto, "IGMP");
                                }
  
+                               if (c_oil->oif_flags[oif_vif_index]
+                                   & PIM_OIF_FLAG_PROTO_VXLAN) {
+                                       strcpy(proto, "VxLAN");
+                               }
                                if (c_oil->oif_flags[oif_vif_index]
                                    & PIM_OIF_FLAG_PROTO_SOURCE) {
                                        strcpy(proto, "SRC");
@@@ -5825,7 -5789,8 +5844,8 @@@ static int pim_cmd_igmp_start(struct vt
        pim_ifp = ifp->info;
  
        if (!pim_ifp) {
-               pim_ifp = pim_if_new(ifp, true, false, false);
+               pim_ifp = pim_if_new(ifp, true, false, false,
+                       false /*vxlan_term*/);
                if (!pim_ifp) {
                        vty_out(vty, "Could not enable IGMP on interface %s\n",
                                ifp->name);
@@@ -6436,7 -6401,8 +6456,8 @@@ static int pim_cmd_interface_add(struc
        struct pim_interface *pim_ifp = ifp->info;
  
        if (!pim_ifp) {
-               pim_ifp = pim_if_new(ifp, false, true, false);
+               pim_ifp = pim_if_new(ifp, false, true, false,
+                       false /*vxlan_term*/);
                if (!pim_ifp) {
                        return 0;
                }
@@@ -7435,6 -7401,29 +7456,29 @@@ DEFUN (no_debug_pim_zebra
        return CMD_SUCCESS;
  }
  
+ DEFUN (debug_pim_vxlan,
+        debug_pim_vxlan_cmd,
+        "debug pim vxlan",
+        DEBUG_STR
+        DEBUG_PIM_STR
+        DEBUG_PIM_VXLAN_STR)
+ {
+       PIM_DO_DEBUG_VXLAN;
+       return CMD_SUCCESS;
+ }
+ DEFUN (no_debug_pim_vxlan,
+        no_debug_pim_vxlan_cmd,
+        "no debug pim vxlan",
+        NO_STR
+        DEBUG_STR
+        DEBUG_PIM_STR
+        DEBUG_PIM_VXLAN_STR)
+ {
+       PIM_DONT_DEBUG_VXLAN;
+       return CMD_SUCCESS;
+ }
  DEFUN (debug_msdp,
         debug_msdp_cmd,
         "debug msdp",
@@@ -8736,6 -8725,369 +8780,369 @@@ DEFUN (show_ip_msdp_sa_sg_vrf_all
        return CMD_SUCCESS;
  }
  
+ struct pim_sg_cache_walk_data {
+       struct vty *vty;
+       json_object *json;
+       json_object *json_group;
+       struct in_addr addr;
+       bool addr_match;
+ };
+ static void pim_show_vxlan_sg_entry(struct pim_vxlan_sg *vxlan_sg,
+                        struct pim_sg_cache_walk_data *cwd)
+ {
+       struct vty *vty = cwd->vty;
+       json_object *json = cwd->json;
+       char src_str[INET_ADDRSTRLEN];
+       char grp_str[INET_ADDRSTRLEN];
+       json_object *json_row;
+       bool installed = (vxlan_sg->up)?TRUE:FALSE;
+       const char *iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-";
+       const char *oif_name;
+       if (pim_vxlan_is_orig_mroute(vxlan_sg))
+               oif_name = vxlan_sg->orig_oif?vxlan_sg->orig_oif->name:"";
+       else
+               oif_name = vxlan_sg->term_oif?vxlan_sg->term_oif->name:"";
+       if (cwd->addr_match && (vxlan_sg->sg.src.s_addr != cwd->addr.s_addr) &&
+                       (vxlan_sg->sg.grp.s_addr != cwd->addr.s_addr)) {
+               return;
+       }
+       pim_inet4_dump("<src?>", vxlan_sg->sg.src, src_str, sizeof(src_str));
+       pim_inet4_dump("<grp?>", vxlan_sg->sg.grp, grp_str, sizeof(grp_str));
+       if (json) {
+               json_object_object_get_ex(json, grp_str, &cwd->json_group);
+               if (!cwd->json_group) {
+                       cwd->json_group = json_object_new_object();
+                       json_object_object_add(json, grp_str,
+                                       cwd->json_group);
+               }
+               json_row = json_object_new_object();
+               json_object_string_add(json_row, "source", src_str);
+               json_object_string_add(json_row, "group", grp_str);
+               json_object_string_add(json_row, "input", iif_name);
+               json_object_string_add(json_row, "output", oif_name);
+               if (installed)
+                       json_object_boolean_true_add(json_row, "installed");
+               else
+                       json_object_boolean_false_add(json_row, "installed");
+               json_object_object_add(cwd->json_group, src_str, json_row);
+       } else {
+               vty_out(vty, "%-15s %-15s %-15s %-15s %-5s\n",
+                               src_str, grp_str, iif_name, oif_name,
+                               installed?"I":"");
+       }
+ }
+ static void pim_show_vxlan_sg_hash_entry(struct hash_backet *backet, void *arg)
+ {
+       pim_show_vxlan_sg_entry((struct pim_vxlan_sg *)backet->data,
+                (struct pim_sg_cache_walk_data *)arg);
+ }
+ static void pim_show_vxlan_sg(struct pim_instance *pim,
+               struct vty *vty, bool uj)
+ {
+       json_object *json = NULL;
+       struct pim_sg_cache_walk_data cwd;
+       if (uj) {
+               json = json_object_new_object();
+       } else {
+               vty_out(vty, "Codes: I -> installed\n");
+               vty_out(vty,
+                       "Source          Group           Input           Output          Flags\n");
+       }
+       memset(&cwd, 0, sizeof(cwd));
+       cwd.vty = vty;
+       cwd.json = json;
+       hash_iterate(pim->vxlan.sg_hash, pim_show_vxlan_sg_hash_entry, &cwd);
+       if (uj) {
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                       json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+ }
+ static void pim_show_vxlan_sg_match_addr(struct pim_instance *pim,
+               struct vty *vty, char *addr_str, bool uj)
+ {
+       json_object *json = NULL;
+       struct pim_sg_cache_walk_data cwd;
+       int result = 0;
+       memset(&cwd, 0, sizeof(cwd));
+       result = inet_pton(AF_INET, addr_str, &cwd.addr);
+       if (result <= 0) {
+               vty_out(vty, "Bad address %s: errno=%d: %s\n", addr_str,
+                               errno, safe_strerror(errno));
+               return;
+       }
+       if (uj) {
+               json = json_object_new_object();
+       } else {
+               vty_out(vty, "Codes: I -> installed\n");
+               vty_out(vty,
+                       "Source          Group           Input           Output          Flags\n");
+       }
+       cwd.vty = vty;
+       cwd.json = json;
+       cwd.addr_match = TRUE;
+       hash_iterate(pim->vxlan.sg_hash, pim_show_vxlan_sg_hash_entry, &cwd);
+       if (uj) {
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                       json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+ }
+ static void pim_show_vxlan_sg_one(struct pim_instance *pim,
+               struct vty *vty, char *src_str, char *grp_str, bool uj)
+ {
+       json_object *json = NULL;
+       struct prefix_sg sg;
+       int result = 0;
+       struct pim_vxlan_sg *vxlan_sg;
+       const char *iif_name;
+       bool installed;
+       const char *oif_name;
+       result = inet_pton(AF_INET, src_str, &sg.src);
+       if (result <= 0) {
+               vty_out(vty, "Bad src address %s: errno=%d: %s\n", src_str,
+                               errno, safe_strerror(errno));
+               return;
+       }
+       result = inet_pton(AF_INET, grp_str, &sg.grp);
+       if (result <= 0) {
+               vty_out(vty, "Bad grp address %s: errno=%d: %s\n", grp_str,
+                               errno, safe_strerror(errno));
+               return;
+       }
+       sg.family = AF_INET;
+       sg.prefixlen = IPV4_MAX_BITLEN;
+       if (uj)
+               json = json_object_new_object();
+       vxlan_sg = pim_vxlan_sg_find(pim, &sg);
+       if (vxlan_sg) {
+               installed = (vxlan_sg->up)?TRUE:FALSE;
+               iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-";
+               if (pim_vxlan_is_orig_mroute(vxlan_sg))
+                       oif_name =
+                               vxlan_sg->orig_oif?vxlan_sg->orig_oif->name:"";
+               else
+                       oif_name =
+                               vxlan_sg->term_oif?vxlan_sg->term_oif->name:"";
+               if (uj) {
+                       json_object_string_add(json, "source", src_str);
+                       json_object_string_add(json, "group", grp_str);
+                       json_object_string_add(json, "input", iif_name);
+                       json_object_string_add(json, "output", oif_name);
+                       if (installed)
+                               json_object_boolean_true_add(json, "installed");
+                       else
+                               json_object_boolean_false_add(json,
+                                       "installed");
+               } else {
+                       vty_out(vty, "SG : %s\n", vxlan_sg->sg_str);
+                       vty_out(vty, "  Input     : %s\n", iif_name);
+                       vty_out(vty, "  Output    : %s\n", oif_name);
+                       vty_out(vty, "  installed : %s\n",
+                               installed?"yes":"no");
+               }
+       }
+       if (uj) {
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                       json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+ }
+ DEFUN (show_ip_pim_vxlan_sg,
+        show_ip_pim_vxlan_sg_cmd,
+        "show ip pim [vrf NAME] vxlan-groups [A.B.C.D [A.B.C.D]] [json]",
+        SHOW_STR
+        IP_STR
+        PIM_STR
+        VRF_CMD_HELP_STR
+        "VxLAN BUM groups\n"
+        "source or group ip\n"
+        "group ip\n"
+        JSON_STR)
+ {
+       bool uj = use_json(argc, argv);
+       struct vrf *vrf;
+       int idx = 2;
+       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       if (!vrf)
+               return CMD_WARNING;
+       char *src_ip = argv_find(argv, argc, "A.B.C.D", &idx) ?
+               argv[idx++]->arg:NULL;
+       char *grp_ip = idx < argc && argv_find(argv, argc, "A.B.C.D", &idx) ?
+               argv[idx]->arg:NULL;
+       if (src_ip && grp_ip)
+               pim_show_vxlan_sg_one(vrf->info, vty, src_ip, grp_ip, uj);
+       else if (src_ip)
+               pim_show_vxlan_sg_match_addr(vrf->info, vty, src_ip, uj);
+       else
+               pim_show_vxlan_sg(vrf->info, vty, uj);
+       return CMD_SUCCESS;
+ }
+ static void pim_show_vxlan_sg_work(struct pim_instance *pim,
+               struct vty *vty, bool uj)
+ {
+       json_object *json = NULL;
+       struct pim_sg_cache_walk_data cwd;
+       struct listnode *node;
+       struct pim_vxlan_sg *vxlan_sg;
+       if (uj) {
+               json = json_object_new_object();
+       } else {
+               vty_out(vty, "Codes: I -> installed\n");
+               vty_out(vty,
+                       "Source          Group           Input           Flags\n");
+       }
+       memset(&cwd, 0, sizeof(cwd));
+       cwd.vty = vty;
+       cwd.json = json;
+       for (ALL_LIST_ELEMENTS_RO(pim_vxlan_p->work_list, node, vxlan_sg))
+               pim_show_vxlan_sg_entry(vxlan_sg, &cwd);
+       if (uj) {
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                       json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+ }
+ DEFUN_HIDDEN (show_ip_pim_vxlan_sg_work,
+               show_ip_pim_vxlan_sg_work_cmd,
+               "show ip pim [vrf NAME] vxlan-work [json]",
+               SHOW_STR
+               IP_STR
+               PIM_STR
+               VRF_CMD_HELP_STR
+               "VxLAN work list\n"
+               JSON_STR)
+ {
+       bool uj = use_json(argc, argv);
+       struct vrf *vrf;
+       int idx = 2;
+       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       if (!vrf)
+               return CMD_WARNING;
+       pim_show_vxlan_sg_work(vrf->info, vty, uj);
+       return CMD_SUCCESS;
+ }
+ DEFUN_HIDDEN (no_ip_pim_mlag,
+        no_ip_pim_mlag_cmd,
+        "no ip pim mlag",
+        NO_STR
+        IP_STR
+        PIM_STR
+        "MLAG\n")
+ {
+       struct in_addr addr;
+       addr.s_addr = 0;
+       pim_vxlan_mlag_update(TRUE /*mlag_enable*/,
+               FALSE /*peer_state*/, PIM_VXLAN_MLAG_ROLE_SECONDARY,
+               NULL/*peerlink*/, &addr);
+       return CMD_SUCCESS;
+ }
+ DEFUN_HIDDEN (ip_pim_mlag,
+        ip_pim_mlag_cmd,
+        "ip pim mlag INTERFACE role [primary|secondary] state [up|down] addr A.B.C.D",
+        IP_STR
+        PIM_STR
+        "MLAG\n"
+        "peerlink sub interface\n"
+        "MLAG role\n"
+        "MLAG role primary\n"
+        "MLAG role secondary\n"
+        "peer session state\n"
+        "peer session state up\n"
+        "peer session state down\n"
+        "configure PIP\n"
+        "unique ip address\n")
+ {
+       struct interface *ifp;
+       const char *peerlink;
+       uint32_t role;
+       int idx;
+       bool peer_state;
+       int result;
+       struct in_addr reg_addr;
+       idx = 3;
+       peerlink = argv[idx]->arg;
+       ifp = if_lookup_by_name(peerlink, VRF_DEFAULT);
+       if (!ifp) {
+               vty_out(vty, "No such interface name %s\n", peerlink);
+               return CMD_WARNING;
+       }
+       idx += 2;
+       if (!strcmp(argv[idx]->arg, "primary")) {
+               role = PIM_VXLAN_MLAG_ROLE_PRIMARY;
+       } else if (!strcmp(argv[idx]->arg, "secondary")) {
+               role = PIM_VXLAN_MLAG_ROLE_SECONDARY;
+       } else {
+               vty_out(vty, "unknown MLAG role %s\n", argv[idx]->arg);
+               return CMD_WARNING;
+       }
+       idx += 2;
+       if (!strcmp(argv[idx]->arg, "up")) {
+               peer_state = TRUE;
+       } else if (strcmp(argv[idx]->arg, "down")) {
+               peer_state = FALSE;
+       } else {
+               vty_out(vty, "unknown MLAG state %s\n", argv[idx]->arg);
+               return CMD_WARNING;
+       }
+       idx += 2;
+       result = inet_pton(AF_INET, argv[idx]->arg, &reg_addr);
+       if (result <= 0) {
+               vty_out(vty, "%% Bad reg address %s: errno=%d: %s\n",
+                       argv[idx]->arg,
+                       errno, safe_strerror(errno));
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       pim_vxlan_mlag_update(TRUE, peer_state, role, ifp, &reg_addr);
+       return CMD_SUCCESS;
+ }
  void pim_cmd_init(void)
  {
        install_node(&interface_node,
        install_element(VRF_NODE, &ip_pim_ecmp_rebalance_cmd);
        install_element(CONFIG_NODE, &no_ip_pim_ecmp_rebalance_cmd);
        install_element(VRF_NODE, &no_ip_pim_ecmp_rebalance_cmd);
+       install_element(CONFIG_NODE, &ip_pim_mlag_cmd);
+       install_element(CONFIG_NODE, &no_ip_pim_mlag_cmd);
  
        install_element(INTERFACE_NODE, &interface_ip_igmp_cmd);
        install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd);
        install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd);
        install_element(ENABLE_NODE, &debug_pim_zebra_cmd);
        install_element(ENABLE_NODE, &no_debug_pim_zebra_cmd);
+       install_element(ENABLE_NODE, &debug_pim_vxlan_cmd);
+       install_element(ENABLE_NODE, &no_debug_pim_vxlan_cmd);
        install_element(ENABLE_NODE, &debug_msdp_cmd);
        install_element(ENABLE_NODE, &no_debug_msdp_cmd);
        install_element(ENABLE_NODE, &debug_msdp_events_cmd);
        install_element(CONFIG_NODE, &no_debug_ssmpingd_cmd);
        install_element(CONFIG_NODE, &debug_pim_zebra_cmd);
        install_element(CONFIG_NODE, &no_debug_pim_zebra_cmd);
+       install_element(CONFIG_NODE, &debug_pim_vxlan_cmd);
+       install_element(CONFIG_NODE, &no_debug_pim_vxlan_cmd);
        install_element(CONFIG_NODE, &debug_msdp_cmd);
        install_element(CONFIG_NODE, &no_debug_msdp_cmd);
        install_element(CONFIG_NODE, &debug_msdp_events_cmd);
        install_element(VIEW_NODE, &show_ip_msdp_mesh_group_vrf_all_cmd);
        install_element(VIEW_NODE, &show_ip_pim_ssm_range_cmd);
        install_element(VIEW_NODE, &show_ip_pim_group_type_cmd);
+       install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_cmd);
+       install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_work_cmd);
        install_element(INTERFACE_NODE, &interface_pim_use_source_cmd);
        install_element(INTERFACE_NODE, &interface_no_pim_use_source_cmd);
        /* Install BFD command */