LCOV - code coverage report
Current view: top level - root/contrail/vrouter/utils - nh.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 135 561 24.1 %
Date: 2026-06-04 02:06:09 Functions: 11 14 78.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * nh.c
       3             :  *
       4             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       5             :  */
       6             : 
       7             : #include <stdio.h>
       8             : #include <stdlib.h>
       9             : #include <unistd.h>
      10             : #include <string.h>
      11             : #include <errno.h>
      12             : #include <stdlib.h>
      13             : #include <stdbool.h>
      14             : #include <getopt.h>
      15             : 
      16             : #include <sys/types.h>
      17             : #include <sys/socket.h>
      18             : 
      19             : #include <net/if.h>
      20             : 
      21             : #if defined(__linux__)
      22             : #include <netinet/ether.h>
      23             : #endif
      24             : 
      25             : #include "vr_types.h"
      26             : #include "vr_nexthop.h"
      27             : #include "vr_os.h"
      28             : #include "nl_util.h"
      29             : #include "ini_parser.h"
      30             : 
      31             : static int8_t src_mac[3][6], dst_mac[3][6], l3_vxlan_mac[6];
      32             : static uint16_t sport, dport;
      33             : static uint32_t nh_id, if_id[3] = {-1, -1, -1}, vrf_id, flags;
      34             : static int nh_set, command, type, dump_marker = -1;
      35             : static int family = AF_INET, count = 0;
      36             : 
      37             : static bool dump_pending = false;
      38             : static int comp_nh[32], lbl[32];
      39             : static int comp_nh_ind = 0, lbl_ind = 0;
      40             : 
      41             : static struct in_addr sip, dip;
      42             : static struct nl_client *cl;
      43             : 
      44             : static int
      45             : vr_nh_op(struct nl_client *cl, int command, int type, uint32_t nh_id,
      46             :         uint32_t *if_id, uint32_t vrf_id, int8_t dst[][6], int8_t src[][6],
      47             :         struct in_addr sip, struct in_addr dip, uint32_t flags);
      48             : 
      49             : char *
      50           4 : nh_type(uint32_t type)
      51             : {
      52           4 :     switch (type) {
      53           0 :     case NH_DEAD:
      54           0 :         return "Dead";
      55             : 
      56           0 :     case NH_RCV:
      57           0 :         return "Receive";
      58             : 
      59           0 :     case NH_L2_RCV:
      60           0 :         return "L2 Receive";
      61             : 
      62           0 :     case NH_ENCAP:
      63           0 :         return "Encap";
      64             : 
      65           4 :     case NH_TUNNEL:
      66           4 :         return "Tunnel";
      67             : 
      68           0 :     case NH_DISCARD:
      69           0 :         return "Drop";
      70             : 
      71           0 :     case NH_RESOLVE:
      72           0 :         return "Resolve";
      73             : 
      74           0 :     case NH_COMPOSITE:
      75           0 :         return "Composite";
      76             : 
      77           0 :     case NH_VRF_TRANSLATE:
      78           0 :         return "Vrf_Translate";
      79             : 
      80           0 :     default:
      81           0 :         return "Invalid";
      82             :     }
      83             : 
      84             :     return NULL;
      85             : }
      86             : 
      87             : char *
      88           0 : nh_ecmp_config_hash_str(uint8_t hash, char *ptr)
      89             : {
      90             :     int i;
      91             : 
      92           0 :     strcpy(ptr,"");
      93           0 :     hash = hash & ((1 << NH_ECMP_CONFIG_HASH_BITS) - 1);
      94           0 :     for (i = 0; i < NH_ECMP_CONFIG_HASH_BITS; i++) {
      95           0 :         switch (hash & (1 << i)) {
      96           0 :         case 0:
      97           0 :             break;
      98           0 :         case NH_ECMP_CONFIG_HASH_PROTO:
      99           0 :             strcat(ptr, "Proto,");
     100           0 :             break;
     101           0 :         case NH_ECMP_CONFIG_HASH_SRC_IP:
     102           0 :             strcat(ptr, "SrcIP,");
     103           0 :             break;
     104           0 :         case NH_ECMP_CONFIG_HASH_SRC_PORT:
     105           0 :             strcat(ptr, "SrcPort,");
     106           0 :             break;
     107           0 :         case NH_ECMP_CONFIG_HASH_DST_IP:
     108           0 :             strcat(ptr, "DstIp,");
     109           0 :             break;
     110           0 :         case NH_ECMP_CONFIG_HASH_DST_PORT:
     111           0 :             strcat(ptr, "DstPort");
     112           0 :             break;
     113           0 :         default:
     114           0 :             strcat(ptr, "Invalid,");
     115           0 :             break;
     116             :         }
     117             :     }
     118             : 
     119           0 :     return ptr;
     120             : }
     121             : 
     122             : char *
     123           4 : nh_flags(uint32_t flags, uint8_t type, char *ptr)
     124             : {
     125             :     int i;
     126             :     uint32_t mask;
     127             : 
     128           4 :     if (!flags) {
     129           0 :         strcpy(ptr, "None");
     130           0 :         return ptr;
     131             :     }
     132             : 
     133             : 
     134           4 :     strcpy(ptr,"");
     135         132 :     for (i = 0, mask = 1; (i < 32); i++, mask = mask << 1) {
     136         128 :         switch(flags & mask) {
     137           4 :         case NH_FLAG_VALID:
     138           4 :             strcat(ptr, "Valid, ");
     139           4 :             break;
     140             : 
     141           0 :         case NH_FLAG_POLICY_ENABLED:
     142           0 :             strcat(ptr, "Policy, ");
     143           0 :             break;
     144             : 
     145           0 :         case NH_FLAG_RELAXED_POLICY:
     146           0 :             strcat(ptr, "Policy(R), ");
     147           0 :             break;
     148             : 
     149           0 :         case NH_FLAG_FLOW_LOOKUP:
     150           0 :             strcat(ptr, "Flow Lookup, ");
     151           0 :             break;
     152             : 
     153           0 :         case NH_FLAG_TUNNEL_GRE:
     154           0 :             if (type == NH_TUNNEL) {
     155           0 :                 if (flags & NH_FLAG_TUNNEL_MPLS_O_MPLS) {
     156           0 :                     strcat(ptr, "MPLSo");
     157             :                 }
     158           0 :                 strcat(ptr, "MPLSoGRE, ");
     159             :             }
     160           0 :             break;
     161             : 
     162           4 :         case NH_FLAG_TUNNEL_UDP_MPLS:
     163           4 :             if (type == NH_TUNNEL) {
     164           4 :                 if (flags & NH_FLAG_TUNNEL_MPLS_O_MPLS) {
     165           0 :                     strcat(ptr, "MPLSo");
     166             :                 }
     167           4 :                 strcat(ptr, "MPLSoUDP, ");
     168             :             }
     169           4 :             break;
     170             : 
     171           0 :         case NH_FLAG_TUNNEL_UDP:
     172           0 :             if (type == NH_TUNNEL)
     173           0 :                 strcat(ptr, "Udp, ");
     174           0 :             break;
     175             : 
     176           0 :         case NH_FLAG_COMPOSITE_ECMP:
     177           0 :             if (type == NH_COMPOSITE)
     178           0 :                 strcat(ptr, "Ecmp, ");
     179           0 :             break;
     180             : 
     181           0 :         case NH_FLAG_COMPOSITE_FABRIC:
     182           0 :             if (type == NH_COMPOSITE)
     183           0 :                 strcat(ptr, "Fabric, ");
     184           0 :             break;
     185             : 
     186           0 :         case NH_FLAG_COMPOSITE_EVPN:
     187           0 :             if (type == NH_COMPOSITE)
     188           0 :                 strcat(ptr, "Evpn, ");
     189           0 :             break;
     190           0 :         case NH_FLAG_COMPOSITE_TOR:
     191           0 :             if (type == NH_COMPOSITE)
     192           0 :                 strcat(ptr, "Tor, ");
     193           0 :             break;
     194             : 
     195           0 :         case NH_FLAG_COMPOSITE_ENCAP:
     196           0 :             if (type == NH_COMPOSITE)
     197           0 :                 strcat(ptr, "Encap, ");
     198           0 :             break;
     199             : 
     200           0 :         case NH_FLAG_MCAST:
     201           0 :             strcat(ptr, "Multicast, ");
     202           0 :             break;
     203             : 
     204           0 :         case NH_FLAG_ROUTE_LOOKUP:
     205           0 :             strcat(ptr, "RouteLookup, ");
     206           0 :             break;
     207             : 
     208           0 :         case NH_FLAG_L3_VXLAN:
     209           0 :             strcat(ptr, "l3_vxlan, ");
     210           0 :             break;
     211             : 
     212           0 :         case NH_FLAG_TUNNEL_VXLAN:
     213           0 :             strcat(ptr, "Vxlan, ");
     214           0 :             break;
     215             : 
     216           0 :         case NH_FLAG_UNKNOWN_UC_FLOOD:
     217           0 :             strcat(ptr, "Unicast Flood, ");
     218           0 :             break;
     219             : 
     220           0 :         case NH_FLAG_TUNNEL_SIP_COPY:
     221           0 :             strcat(ptr, "Copy SIP, ");
     222           0 :             break;
     223             : 
     224           0 :         case NH_FLAG_TUNNEL_PBB:
     225           0 :             strcat(ptr, "Pbb, ");
     226           0 :             break;
     227             : 
     228           0 :         case NH_FLAG_INDIRECT:
     229           0 :             strcat(ptr, "Indirect, ");
     230           0 :             break;
     231             : 
     232           4 :         case NH_FLAG_ETREE_ROOT:
     233           4 :             strcat(ptr, "Etree Root, ");
     234           4 :             break;
     235             : 
     236           0 :         case NH_FLAG_MAC_LEARN:
     237           0 :             strcat(ptr, "Mac Learn, ");
     238           0 :             break;
     239             : 
     240           0 :         case NH_FLAG_L2_CONTROL_DATA:
     241           0 :             strcat(ptr, "Evpn Control Word, ");
     242           0 :             break;
     243             : 
     244           0 :         case NH_FLAG_CRYPT_TRAFFIC:
     245           0 :             strcat(ptr, "Encrypt Traffic, ");
     246           0 :             break;
     247             : 
     248           4 :         case NH_FLAG_TUNNEL_UNDERLAY_ECMP:
     249           4 :             strcat(ptr, "Underlay Ecmp, ");
     250           4 :             break;
     251             :         }
     252             :     }
     253             : 
     254           4 :     return ptr;
     255             : }
     256             : 
     257             : static void
     258          20 : nh_print_newline_header(void)
     259             : {
     260          20 :     printf("\n%14c", ' ');
     261          20 :     return;
     262             : }
     263             : 
     264             : static void
     265           4 : nexthop_req_process(void *s_req)
     266             : {
     267           4 :     unsigned int i, j, printed = 0;
     268             :     struct in_addr a;
     269             :     char flags_mem[500];
     270             :     char fam[100];
     271           4 :     char in6_dst[INET6_ADDRSTRLEN] = { 0 };
     272             : 
     273           4 :     vr_nexthop_req *req = (vr_nexthop_req *)(s_req);
     274             : 
     275           4 :     if (req->nhr_family == AF_INET)
     276           4 :         strcpy(fam, "AF_INET");
     277           0 :     else if (req->nhr_family == AF_INET6)
     278           0 :         strcpy(fam, "AF_INET6");
     279           0 :     else if (req->nhr_family == AF_BRIDGE)
     280           0 :         strcpy(fam, "AF_BRIDGE");
     281           0 :     else if (req->nhr_family == AF_UNSPEC)
     282           0 :         strcpy(fam, "AF_UNSPEC");
     283             :     else
     284           0 :         strcpy(fam, "N/A");
     285             : 
     286           4 :     printf("Id:%-9d  Type:%-13s  Fmly:%8s  Rid:%d  Ref_cnt:%-10d Vrf:%d",
     287           4 :                 req->nhr_id, nh_type(req->nhr_type), fam,
     288             :                 req->nhr_rid, req->nhr_ref_cnt, req->nhr_vrf);
     289           4 :     nh_print_newline_header();
     290           4 :     printf("Flags:%s",
     291           4 :             nh_flags(req->nhr_flags, req->nhr_type, flags_mem));
     292             : 
     293           4 :     if ((req->nhr_flags & NH_FLAG_INDIRECT) && (req->nhr_nh_list_size)) {
     294           0 :         i = -1;
     295           0 :         if (req->nhr_label_list_size)
     296           0 :             i = req->nhr_label_list[0];
     297           0 :         nh_print_newline_header();
     298           0 :         printf("Direct NH(label): %d(%d)", req->nhr_nh_list[0], i);
     299             :     }
     300             : 
     301           4 :     if (req->nhr_type == NH_RCV) {
     302           0 :         nh_print_newline_header();
     303           0 :         printf("Oif:%d", req->nhr_encap_oif_id[0]);
     304           4 :     } else if (req->nhr_type == NH_ENCAP) {
     305           0 :         nh_print_newline_header();
     306           0 :         printf("EncapFmly:%04x Oif:%d Len:%d", req->nhr_encap_family,
     307           0 :                 req->nhr_encap_oif_id[0], req->nhr_encap_size);
     308           0 :         nh_print_newline_header();
     309           0 :         printf("Encap Data: ");
     310           0 :         for (i = 0; i< req->nhr_encap_size; i++) {
     311           0 :             printf("%02x ", (unsigned char)req->nhr_encap[i]);
     312             :         }
     313           4 :     } else if (req->nhr_type == NH_TUNNEL) {
     314           4 :         nh_print_newline_header();
     315           4 :         if (!(req->nhr_flags & NH_FLAG_TUNNEL_PBB)) {
     316           4 :             if (req->nhr_flags & NH_FLAG_TUNNEL_UNDERLAY_ECMP) {
     317          16 :                 for (i = 0; i < req->nhr_encap_oif_id_size; i++) {
     318          12 :                     printf("Oif:%d EncapValid:%d ",
     319          12 :                             req->nhr_encap_oif_id[i], req->nhr_encap_valid[i]);
     320          12 :                     if (req->nhr_encap_valid[i]) {
     321           6 :                         printf("Len:%d Data:", req->nhr_encap_len);
     322          90 :                         for (j = 0; j < req->nhr_encap_len; j++) {
     323          84 :                             printf("%02x ",
     324          84 :                         (unsigned char)req->nhr_encap[i*req->nhr_encap_len+j]);
     325             :                         }
     326             :                     } else
     327           6 :                         printf("Len:0 Data:NULL");
     328          12 :                     nh_print_newline_header();
     329             :                 }
     330             :             } else {
     331           0 :                 printf("Oif:%d Len:%d Data:",
     332           0 :                         req->nhr_encap_oif_id[0], req->nhr_encap_size);
     333           0 :                 for (i = 0; i< req->nhr_encap_size; i++) {
     334           0 :                     printf("%02x ", (unsigned char)req->nhr_encap[i]);
     335             :                 }
     336           0 :                 nh_print_newline_header();
     337             :             }
     338             :         }
     339           4 :         if (!(req->nhr_flags & NH_FLAG_TUNNEL_PBB)) {
     340           4 :             if (req->nhr_family == AF_INET) {
     341           4 :                 a.s_addr = req->nhr_tun_sip;
     342           4 :                 printf("Sip:%s", inet_ntoa(a));
     343           4 :                 a.s_addr = req->nhr_tun_dip;
     344           4 :                 printf(" Dip:%s", inet_ntoa(a));
     345           0 :             } else if (req->nhr_family == AF_INET6) {
     346           0 :                 printf("Sip: %s",
     347           0 :                     inet_ntop(AF_INET6, (struct in6_addr *)req->nhr_tun_sip6,
     348             :                     in6_dst, sizeof(in6_dst)));
     349           0 :                 printf(" Dip: %s",
     350           0 :                     inet_ntop(AF_INET6, (struct in6_addr *)req->nhr_tun_dip6,
     351             :                     in6_dst, sizeof(in6_dst)));
     352             :             }
     353             :         }
     354             : 
     355           4 :         if (req->nhr_flags & NH_FLAG_TUNNEL_UDP) {
     356           0 :             nh_print_newline_header();
     357           0 :             printf("Sport:%d Dport:%d\n", ntohs(req->nhr_tun_sport),
     358           0 :                                                   ntohs(req->nhr_tun_dport));
     359             :         }
     360             : 
     361           4 :         if (req->nhr_flags & NH_FLAG_L3_VXLAN)
     362           0 :             printf(" L3_Vxlan_Mac: "MAC_FORMAT,
     363           0 :                     MAC_VALUE((uint8_t*)req->nhr_rw_dst_mac));
     364             : 
     365           4 :         if (req->nhr_flags & NH_FLAG_TUNNEL_PBB) {
     366           0 :             i = -1;
     367           0 :             if (req->nhr_label_list_size)
     368           0 :                 i = req->nhr_label_list[0];
     369           0 :             printf("Bmac:"MAC_FORMAT " Label:%d",
     370           0 :                     MAC_VALUE((uint8_t *)req->nhr_pbb_mac), i);
     371             :         }
     372           4 :         if(req->nhr_flags & NH_FLAG_TUNNEL_MPLS_O_MPLS) {
     373           0 :             printf(" Transport Label:%u", req->nhr_transport_label);
     374             :         }
     375           4 :         if (req->nhr_encap_crypt_oif_id != (int)-1 &&
     376           4 :             req->nhr_encap_crypt_oif_id != 0) {
     377           0 :             nh_print_newline_header();
     378           0 :             printf("CryptOif:%d\n", req->nhr_encap_crypt_oif_id);
     379             :         }
     380           0 :     } else if (req->nhr_type == NH_VRF_TRANSLATE) {
     381           0 :         nh_print_newline_header();
     382           0 :         printf("Vrf:%d", req->nhr_vrf);
     383           0 :     } else if (req->nhr_type == NH_COMPOSITE) {
     384           0 :         if (req->nhr_flags & NH_FLAG_COMPOSITE_ECMP) {
     385           0 :             if (req->nhr_ecmp_config_hash) {
     386           0 :                 nh_print_newline_header();
     387           0 :                 nh_ecmp_config_hash_str(req->nhr_ecmp_config_hash, flags_mem);
     388           0 :                 printf("Valid Hash Key Parameters: %s", flags_mem);
     389             :             }
     390             :         }
     391           0 :         nh_print_newline_header();
     392           0 :         printf("Sub NH(label):");
     393           0 :         for (i = 0; i < req->nhr_nh_list_size; i++) {
     394           0 :             if (printed > 60) {
     395           0 :                 nh_print_newline_header();
     396           0 :                 printf("%14c", ' ');
     397           0 :                 printed = 0;
     398             :             }
     399           0 :             printed += printf(" %d", req->nhr_nh_list[i]);
     400           0 :             if (req->nhr_label_list[i] >= 0)
     401           0 :                 printed += printf("(%d)", req->nhr_label_list[i]);
     402             :         }
     403             : 
     404           0 :         if (req->nhr_nh_count &&
     405           0 :                 (req->nhr_nh_count - req->nhr_nh_list_size)) {
     406           0 :             printf(" and %u more components...\n",
     407           0 :                     req->nhr_nh_count - req->nhr_nh_list_size);
     408             :         }
     409             :     }
     410             : 
     411           4 :     if (command == SANDESH_OP_DUMP) {
     412           0 :         dump_marker = req->nhr_id;
     413             :     }
     414             : 
     415           4 :     printf("\n\n");
     416           4 :     if (command == SANDESH_OP_GET) {
     417           4 :         if (req->nhr_type == NH_COMPOSITE) {
     418           0 :             for (i = 0; i < req->nhr_nh_list_size; i++) {
     419             :                 // Skip expanding sub-nh for -1 index
     420           0 :                 if (req->nhr_nh_list[i] == -1)
     421           0 :                     continue;
     422           0 :                 vr_nh_op(cl, command, type, req->nhr_nh_list[i], if_id, vrf_id,
     423             :                              dst_mac, src_mac, sip, dip, flags);
     424             :             }
     425             :         }
     426             : 
     427           4 :         if ((req->nhr_flags & NH_FLAG_INDIRECT) && (req->nhr_nh_list_size)) {
     428           0 :             vr_nh_op(cl, command, type, req->nhr_nh_list[0], if_id, vrf_id,
     429             :                          dst_mac, src_mac, sip, dip, flags);
     430             :         }
     431             :     }
     432           4 : }
     433             : 
     434             : static void
     435           4 : response_process(void *s)
     436             : {
     437           4 :     vr_response_common_process((vr_response *)s, &dump_pending);
     438           4 :     return;
     439             : }
     440             : 
     441             : static void
     442           4 : nh_fill_nl_callbacks()
     443             : {
     444           4 :     nl_cb.vr_response_process = response_process;
     445           4 :     nl_cb.vr_nexthop_req_process = nexthop_req_process;
     446           4 : }
     447             : 
     448             : static int
     449           4 : vr_nh_op(struct nl_client *cl, int command, int type, uint32_t nh_id,
     450             :         uint32_t *if_id, uint32_t vrf_id, int8_t dst[][6], int8_t src[][6],
     451             :         struct in_addr sip, struct in_addr dip, uint32_t flags)
     452             : {
     453             :     int ret;
     454           4 :     bool dump = false;
     455             : 
     456           4 : op_retry:
     457           4 :     switch (command) {
     458           0 :     case SANDESH_OP_ADD:
     459           0 :         if (flags & NH_FLAG_TUNNEL_PBB) {
     460           0 :             ret = vr_send_pbb_tunnel_add(cl, 0, nh_id, flags,
     461           0 :                     vrf_id, dst[0], comp_nh[0], lbl[0]);
     462           0 :         } else if ((type == NH_ENCAP) || (type == NH_TUNNEL)) {
     463           0 :             ret = vr_send_nexthop_encap_tunnel_add(cl, 0, type, nh_id,
     464             :                     flags, vrf_id, if_id, src, dst, sip, dip, sport, dport, l3_vxlan_mac, family, count);
     465           0 :         } else if (type == NH_COMPOSITE) {
     466           0 :             ret = vr_send_nexthop_composite_add(cl, 0, nh_id, flags, vrf_id,
     467             :                     comp_nh_ind, comp_nh, lbl, family);
     468             :         } else {
     469           0 :             ret = vr_send_nexthop_add(cl, 0, type, nh_id, flags, vrf_id, if_id, family);
     470             :         }
     471             : 
     472           0 :         break;
     473             : 
     474           0 :     case SANDESH_OP_DEL:
     475           0 :         ret = vr_send_nexthop_delete(cl, 0, nh_id);
     476           0 :         break;
     477             : 
     478           0 :     case SANDESH_OP_DUMP:
     479           0 :         dump = true;
     480           0 :         ret = vr_send_nexthop_dump(cl, 0, dump_marker);
     481           0 :        break;
     482             : 
     483           4 :     case SANDESH_OP_GET:
     484           4 :         ret = vr_send_nexthop_get(cl, 0, nh_id);
     485           4 :         break;
     486             : 
     487           0 :     default:
     488           0 :         ret = -EINVAL;
     489             :     }
     490             : 
     491           4 :     if (ret < 0)
     492           0 :         return ret;
     493             : 
     494           4 :     ret = vr_recvmsg(cl, dump);
     495           4 :     if (ret <= 0)
     496           0 :         return ret;
     497             : 
     498           4 :     if (dump_pending)
     499           0 :         goto op_retry;
     500             : 
     501           4 :     return 0;
     502             : }
     503             : 
     504             : void
     505           0 : cmd_usage()
     506             : {
     507           0 :     printf("Usage: [--create <nhid> create nexthop\n"
     508             :            "       [--delete <nhid> delete nexthop\n"
     509             :            "       [--vrf <vrf_id> ]\n"
     510             :            "       [--pol NH with policy]\n"
     511             :            "       [--rpol NH with relaxed policy]\n"
     512             :            "       [--l2 NH with family bridge]\n"
     513             :            "       [--root NH is an Etree Root]\n"
     514             :            "       [--rlkup Force Route Lookup]\n"
     515             :            "       [--type <type> type of the tunnel 1 - rcv, 2 - encap \n"
     516             :            "                       3 - tunnel, 4 - resolve, 5 - discard, 6 - Composite\n"
     517             :            "                       7 - VRF Translate, 8 - L2 Rcv NH] \n"
     518             :            "                [RCV_NH options]\n"
     519             :            "                    [--oif <if_id> out going interface index]\n"
     520             :            "                [L2RCV_NH options]\n"
     521             :            "                    [--oif <if_id> out going interface index]\n"
     522             :            "                [ENCAP_NH optionsi - default L3]\n"
     523             :            "                    [--mc multicast nh]\n"
     524             :            "                    [--smac <xx:xx:xx:xx:xx:xx> source mac ]\n"
     525             :            "                    [--dmac <xx:xx:xx:xx:xx:xx> destination mac ]\n"
     526             :            "                    [--oif = out going interface index]\n"
     527             :            "                [TUNNEL_NH options - default Gre]\n"
     528             :            "                    [--pbb PBB tunnel options]\n"
     529             :            "                        [--cni <nh_id> direct nh member id]\n"
     530             :            "                        [--lbl <lbl> Evpn label for PBB tunnel]\n"
     531             :            "                        [--dmac <xx:xx:xx:xx:xx:xx> destination Bmac]\n"
     532             :            "                        [--ind indirect flag]\n"
     533             :            "                    [--oif <if_id1,if_id2,...> comma seperated list of out going interface indices(max 3 in case of L3 multihoming)]\n"
     534             :            "                    [--smac <xx:xx:xx:xx:xx:xx,xx:xx:xx:xx:xx:xx,...> comma seperated list of source mac(max 3 in case of L3 multihoming)]\n"
     535             :            "                    [--dmac <xx:xx:xx:xx:xx:xx,xx:xx:xx:xx:xx:xx,...> comma seperated list of destination mac(max 3 in case of L3 multihoming)]\n"
     536             :            "                    [--sip <x.x.x.x> source ip of tunnel] \n"
     537             :            "                    [--dip <x.x.x.x> destination ip of tunnel ]\n"
     538             :            "                    [--udp Udptunnel ]\n"
     539             :            "                        [--sport <port> source port of udp tunnel]\n"
     540             :            "                        [--dport <port> destination port of udp tunnel]\n"
     541             :            "                    [--vxlan Vxlan Tunnel]\n"
     542             :            "                        [--sport <port> source port of vxlan tunnel]\n"
     543             :            "                        [--dport <port> destination port of vxlan tunnel]\n"
     544             :            "                        [--l3_vxlan <xx:xx:xx:xx:xx:xx> Remote EVPN-5 vRouter mac]\n"
     545             :            "                [RESOLVE_NH options]\n"
     546             :            "                [DISCARD_NH options]\n"
     547             :            "                [COMPOSITE_NH options]\n"
     548             :            "                    [--cni <nh_id> composite nexthop member id]\n"
     549             :            "                    [--cmc composite multicast nexhop]\n"
     550             :            "                    [--cfa composit fabric ]\n"
     551             :            "                    [--cen composit encap ]\n"
     552             :            "                    [--cevpn composit evpn ]\n"
     553             :            "                        [--lbl <lbl> label for composit fabric ]\n"
     554             :            "                    [--tor composit tor ]\n"
     555             :            "                        [--lbl <lbl> label for composit fabric ]\n"
     556             :            "                    [--ecmp composite ecmp nexthop]\n"
     557             :            "                [VRF Translate options]\n"
     558             :            "                    [--vxlan Vxlan VRF Translation]\n"
     559             :            "                    [--uucf Unknown Unicast Flood]\n");
     560           0 :     exit(-EINVAL);
     561             : }
     562             : 
     563             : void
     564           0 : usage()
     565             : {
     566           0 :     printf("Usage: nh --list\n"
     567             :            "       nh --get <nh_id>\n"
     568             :            "       nh --help\n\n"
     569             :            "--list Lists All Nexthops\n"
     570             :            "--get  <nh_id> Displays nexthop corresponding to <nh_id>\n"
     571             :            "--sock-dir <netlink sock dir>\n"
     572             :            "--help Displays this help message\n\n");
     573             : 
     574           0 :     exit(-EINVAL);
     575             : }
     576             : 
     577             : enum opt_index {
     578             :     OIF_OPT_IND,
     579             :     SMAC_OPT_IND,
     580             :     DMAC_OPT_IND,
     581             :     VRF_OPT_IND,
     582             :     TYPE_OPT_IND,
     583             :     SIP_OPT_IND,
     584             :     DIP_OPT_IND,
     585             :     POL_OPT_IND,
     586             :     RPOL_OPT_IND,
     587             :     L2_OPT_IND,
     588             :     SPORT_OPT_IND,
     589             :     DPORT_OPT_IND,
     590             :     L3_VXLAN_OPT_IND,
     591             :     UDP_OPT_IND,
     592             :     VXLAN_OPT_IND,
     593             :     CNI_OPT_IND,
     594             :     CMC_OPT_IND,
     595             :     CFA_OPT_IND,
     596             :     MC_OPT_IND,
     597             :     CEN_OPT_IND,
     598             :     CEVPN_OPT_IND,
     599             :     TOR_OPT_IND,
     600             :     RLKUP_OPT_IND,
     601             :     LBL_OPT_IND,
     602             :     UUCF_OPT_IND,
     603             :     LST_OPT_IND,
     604             :     GET_OPT_IND,
     605             :     CRT_OPT_IND,
     606             :     DEL_OPT_IND,
     607             :     CMD_OPT_IND,
     608             :     IND_OPT_IND,
     609             :     PBB_OPT_IND,
     610             :     ROOT_OPT_IND,
     611             :     ML_OPT_IND,
     612             :     HLP_OPT_IND,
     613             :     SOCK_DIR_OPT_IND,
     614             :     ECMP_OPT_IND,
     615             :     MAX_OPT_IND
     616             : };
     617             : 
     618             : static int opt[MAX_OPT_IND], zero_opt[MAX_OPT_IND];
     619             : 
     620             : static bool
     621          16 : opt_set(int ind)
     622             : {
     623          16 :     if (ind < 0 || ind >= MAX_OPT_IND)
     624           0 :         return false;
     625             : 
     626          16 :     if (opt[ind]) {
     627           8 :         opt[ind] = 0;
     628           8 :         return true;
     629             :     }
     630             : 
     631           8 :     return false;
     632             : }
     633             : 
     634             : static struct option long_options[] = {
     635             :     [OIF_OPT_IND]       = {"oif",   required_argument,  &opt[OIF_OPT_IND],      1},
     636             :     [SMAC_OPT_IND]      = {"smac",  required_argument,  &opt[SMAC_OPT_IND],     1},
     637             :     [DMAC_OPT_IND]      = {"dmac",  required_argument,  &opt[DMAC_OPT_IND],     1},
     638             :     [VRF_OPT_IND]       = {"vrf",   required_argument,  &opt[VRF_OPT_IND],      1},
     639             :     [TYPE_OPT_IND]      = {"type",  required_argument,  &opt[TYPE_OPT_IND],     1},
     640             :     [SIP_OPT_IND]       = {"sip",   required_argument,  &opt[SIP_OPT_IND],      1},
     641             :     [DIP_OPT_IND]       = {"dip",   required_argument,  &opt[DIP_OPT_IND],      1},
     642             :     [POL_OPT_IND]       = {"pol",   no_argument,        &opt[POL_OPT_IND],      1},
     643             :     [RPOL_OPT_IND]      = {"rpol",  no_argument,        &opt[RPOL_OPT_IND],     1},
     644             :     [L2_OPT_IND]        = {"l2",    no_argument,        &opt[L2_OPT_IND],       1},
     645             :     [SPORT_OPT_IND]     = {"sport", required_argument,  &opt[SPORT_OPT_IND],    1},
     646             :     [DPORT_OPT_IND]     = {"dport", required_argument,  &opt[DPORT_OPT_IND],    1},
     647             :     [L3_VXLAN_OPT_IND]  = {"l3_vxlan", required_argument, &opt[L3_VXLAN_OPT_IND], 1},
     648             :     [UDP_OPT_IND]       = {"udp",   no_argument,        &opt[UDP_OPT_IND],      1},
     649             :     [VXLAN_OPT_IND]     = {"vxlan", no_argument,        &opt[VXLAN_OPT_IND],    1},
     650             :     [CNI_OPT_IND]       = {"cni",   required_argument,  &opt[CNI_OPT_IND],      1},
     651             :     [CMC_OPT_IND]       = {"cmc",   no_argument,        &opt[CMC_OPT_IND],      1},
     652             :     [CFA_OPT_IND]       = {"cfa",   no_argument,        &opt[CFA_OPT_IND],      1},
     653             :     [MC_OPT_IND]        = {"mc",    no_argument,        &opt[MC_OPT_IND],       1},
     654             :     [CEN_OPT_IND]       = {"cen",   no_argument,        &opt[CEN_OPT_IND],      1},
     655             :     [CEVPN_OPT_IND]     = {"cevpn", no_argument,        &opt[CEVPN_OPT_IND],    1},
     656             :     [TOR_OPT_IND]       = {"tor",   no_argument,        &opt[TOR_OPT_IND],      1},
     657             :     [RLKUP_OPT_IND]     = {"rlkup", no_argument,        &opt[RLKUP_OPT_IND],    1},
     658             :     [LBL_OPT_IND]       = {"lbl",   required_argument,  &opt[LBL_OPT_IND],      1},
     659             :     [UUCF_OPT_IND]      = {"uucf",  no_argument,        &opt[UUCF_OPT_IND],     1},
     660             :     [LST_OPT_IND]       = {"list",  no_argument,        &opt[LST_OPT_IND],      1},
     661             :     [GET_OPT_IND]       = {"get",   required_argument,  &opt[GET_OPT_IND],      1},
     662             :     [CRT_OPT_IND]       = {"create", required_argument, &opt[CRT_OPT_IND],      1},
     663             :     [DEL_OPT_IND]       = {"delete", required_argument, &opt[DEL_OPT_IND],      1},
     664             :     [CMD_OPT_IND]       = {"cmd",   no_argument,        &opt[CMD_OPT_IND],      1},
     665             :     [IND_OPT_IND]       = {"ind",   no_argument,        &opt[IND_OPT_IND],      1},
     666             :     [PBB_OPT_IND]       = {"pbb",   no_argument,        &opt[PBB_OPT_IND],      1},
     667             :     [ROOT_OPT_IND]      = {"root",  no_argument,        &opt[ROOT_OPT_IND],     1},
     668             :     [ML_OPT_IND]        = {"ml",    no_argument,        &opt[ML_OPT_IND],       1},
     669             :     [HLP_OPT_IND]       = {"help",  no_argument,        &opt[HLP_OPT_IND],      1},
     670             :     [SOCK_DIR_OPT_IND]  = {"sock-dir", required_argument, &opt[SOCK_DIR_OPT_IND], 1},
     671             :     [ECMP_OPT_IND]      = {"ecmp", no_argument,         &opt[ECMP_OPT_IND],     1},
     672             :     [MAX_OPT_IND]       = { NULL,   0,                  0,                      0}
     673             : };
     674             : 
     675             : static void
     676           8 : parse_long_opts(int ind, char *opt_arg)
     677             : {
     678             :     int errno, i;
     679             :     char *c;
     680             :     struct ether_addr *mac;
     681             : 
     682           8 :     errno = 0;
     683           8 :     switch (ind) {
     684           0 :     case CMD_OPT_IND:
     685           0 :         cmd_usage();
     686           0 :         break;
     687             : 
     688           0 :     case HLP_OPT_IND:
     689           0 :         usage();
     690           0 :         break;
     691             : 
     692           4 :     case GET_OPT_IND:
     693             :     case CRT_OPT_IND:
     694             :     case DEL_OPT_IND:
     695           4 :         nh_id = strtoul(opt_arg, NULL, 0);
     696           4 :         if (errno)
     697           0 :             usage();
     698           4 :         nh_set = 1;
     699           4 :         break;
     700             : 
     701           0 :     case OIF_OPT_IND:
     702           0 :         count = 0;
     703           0 :         i = 0;
     704           0 :         c = strtok(opt_arg, ",");
     705           0 :         while (c != NULL) {
     706           0 :             if_id[i++] = atoi(c);
     707           0 :             c = strtok(NULL, ",");
     708             :         }
     709           0 :         count = i;
     710           0 :         if (errno)
     711           0 :             usage();
     712           0 :         break;
     713             : 
     714           0 :     case SMAC_OPT_IND:
     715           0 :         c = NULL;
     716           0 :         i = 0;
     717           0 :         c = strtok(opt_arg, ",");
     718           0 :         while (c != NULL) {
     719           0 :             mac = ether_aton(c);
     720           0 :             if (mac)
     721           0 :                 memcpy(src_mac[i], mac, sizeof(src_mac[i]));
     722             :             else
     723           0 :                 cmd_usage();
     724           0 :             c = strtok(NULL, ",");
     725           0 :             i++;
     726           0 :             mac = NULL;
     727             :         }
     728           0 :         break;
     729             : 
     730           0 :     case DMAC_OPT_IND:
     731           0 :         c = NULL;
     732           0 :         i = 0;
     733           0 :         c = strtok(opt_arg, ",");
     734           0 :         while (c != NULL) {
     735           0 :             mac = ether_aton(c);
     736           0 :             if (mac)
     737           0 :                 memcpy(dst_mac[i], mac, sizeof(dst_mac[i]));
     738             :             else
     739           0 :                 cmd_usage();
     740           0 :             c = strtok(NULL, ",");
     741           0 :             i++;
     742           0 :             mac = NULL;
     743             :         }
     744           0 :         break;
     745             : 
     746           0 :     case VRF_OPT_IND:
     747           0 :         vrf_id = strtoul(opt_arg, NULL, 0);
     748           0 :         if (errno)
     749           0 :             usage();
     750           0 :         break;
     751             : 
     752           0 :     case TYPE_OPT_IND:
     753           0 :         type = strtoul(opt_arg, NULL, 0);
     754           0 :         if (errno)
     755           0 :             usage();
     756           0 :         break;
     757             : 
     758           0 :     case SIP_OPT_IND:
     759           0 :         inet_aton(opt_arg, &sip);
     760           0 :         break;
     761             : 
     762           0 :     case DIP_OPT_IND:
     763           0 :         inet_aton(opt_arg, &dip);
     764           0 :         break;
     765             : 
     766           0 :     case SPORT_OPT_IND:
     767           0 :         sport = strtoul(opt_arg, NULL, 0);
     768           0 :         if (errno)
     769           0 :             usage();
     770           0 :         break;
     771             : 
     772           0 :     case CNI_OPT_IND:
     773           0 :         comp_nh[comp_nh_ind++] = strtoul(opt_arg, NULL, 0);
     774           0 :         if (errno)
     775           0 :             usage();
     776           0 :         break;
     777             : 
     778           0 :     case LBL_OPT_IND:
     779           0 :         lbl[lbl_ind++] = strtoul(opt_arg, NULL, 0);
     780           0 :         if (errno)
     781           0 :             usage();
     782           0 :         break;
     783             : 
     784           0 :     case DPORT_OPT_IND:
     785           0 :         dport = strtoul(opt_arg, NULL, 0);
     786           0 :         if (errno)
     787           0 :             usage();
     788           0 :         break;
     789           0 :     case L3_VXLAN_OPT_IND:
     790           0 :         mac = ether_aton(opt_arg);
     791           0 :         if (mac)
     792           0 :             memcpy(l3_vxlan_mac, mac, sizeof(l3_vxlan_mac));
     793             :         else
     794           0 :             cmd_usage();
     795           0 :         break;
     796           4 :     case SOCK_DIR_OPT_IND:
     797           4 :         vr_socket_dir = opt_arg;
     798           4 :         break;
     799             :     }
     800             : 
     801           8 :     return;
     802             : }
     803             : 
     804             : static void
     805           4 : validate_options(void)
     806             : {
     807           4 :     if (opt_set(CRT_OPT_IND)) {
     808           0 :         command = SANDESH_OP_ADD;
     809           4 :     } else if (opt_set(DEL_OPT_IND)) {
     810           0 :         command = SANDESH_OP_DEL;
     811           4 :     } else if (opt_set(GET_OPT_IND)) {
     812           4 :         command = SANDESH_OP_GET;
     813           0 :     } else if (opt_set(LST_OPT_IND)) {
     814           0 :         command = SANDESH_OP_DUMP;
     815             :     } else {
     816           0 :         usage();
     817           0 :         return;
     818             :     }
     819             : 
     820             : 
     821           4 :     switch (command) {
     822           0 :     case SANDESH_OP_ADD:
     823           0 :         if (!nh_set)
     824           0 :             cmd_usage();
     825             : 
     826           0 :         flags |= NH_FLAG_VALID;
     827           0 :         if (!opt_set(TYPE_OPT_IND))
     828           0 :             cmd_usage();
     829             : 
     830           0 :          if(!opt_set(VRF_OPT_IND))
     831           0 :             cmd_usage();
     832             : 
     833           0 :         if (opt_set(MC_OPT_IND))
     834           0 :             flags |= NH_FLAG_MCAST;
     835             : 
     836           0 :         if (opt_set(POL_OPT_IND))
     837           0 :             flags |= NH_FLAG_POLICY_ENABLED;
     838             : 
     839           0 :         if (opt_set(IND_OPT_IND))
     840           0 :             flags |= NH_FLAG_INDIRECT;
     841             : 
     842           0 :         if (opt_set(RPOL_OPT_IND))
     843           0 :             flags |= NH_FLAG_RELAXED_POLICY;
     844             : 
     845           0 :         if (opt_set(L2_OPT_IND))
     846           0 :             family = AF_BRIDGE;
     847             : 
     848           0 :         if (opt_set(RLKUP_OPT_IND))
     849           0 :             flags |= NH_FLAG_ROUTE_LOOKUP;
     850             : 
     851           0 :         if (opt_set(ML_OPT_IND))
     852           0 :             flags |= NH_FLAG_MAC_LEARN;
     853             : 
     854           0 :         if (opt_set(ROOT_OPT_IND))
     855           0 :             flags |= NH_FLAG_ETREE_ROOT;
     856             : 
     857           0 :         if (type == NH_RCV) {
     858           0 :             if (!opt_set(OIF_OPT_IND))
     859           0 :                 cmd_usage();
     860             : 
     861           0 :             if (memcmp(opt, zero_opt, sizeof(opt)))
     862           0 :                 cmd_usage();
     863           0 :         } else if (type == NH_L2_RCV) {
     864           0 :             if (memcmp(opt, zero_opt, sizeof(opt)))
     865           0 :                 cmd_usage();
     866           0 :         } else if (type == NH_ENCAP) {
     867           0 :             if (!opt_set(OIF_OPT_IND))
     868           0 :                 cmd_usage();
     869             : 
     870           0 :             if (family == AF_INET) {
     871           0 :                 if (!opt_set(SMAC_OPT_IND) || !opt_set(DMAC_OPT_IND))
     872           0 :                     cmd_usage();
     873             : 
     874           0 :                 if (opt_set(L3_VXLAN_OPT_IND))
     875           0 :                     flags |= NH_FLAG_L3_VXLAN;
     876             : 
     877           0 :                 if (memcmp(opt, zero_opt, sizeof(opt)))
     878           0 :                     cmd_usage();
     879             :             }
     880             : 
     881           0 :         } else if (type == NH_TUNNEL) {
     882             : 
     883           0 :             if (count > 1)
     884           0 :                 flags |= NH_FLAG_TUNNEL_UNDERLAY_ECMP;
     885             : 
     886           0 :             if (opt_set(PBB_OPT_IND)) {
     887           0 :                 if (!opt_set(CNI_OPT_IND)) {
     888           0 :                     cmd_usage();
     889             :                 }
     890             : 
     891           0 :                 if (comp_nh_ind != 1)
     892           0 :                     cmd_usage();
     893             : 
     894           0 :                 if (!opt_set(LBL_OPT_IND) || !opt_set(DMAC_OPT_IND))
     895           0 :                     cmd_usage();
     896             : 
     897           0 :                 flags |= NH_FLAG_TUNNEL_PBB;
     898             : 
     899           0 :             } else if (!opt_set(OIF_OPT_IND) || !opt_set(SMAC_OPT_IND) ||
     900           0 :                     !opt_set(DMAC_OPT_IND) || !opt_set(SIP_OPT_IND) ||
     901           0 :                     !opt_set(DIP_OPT_IND)) {
     902           0 :                 cmd_usage();
     903             :             }
     904             : 
     905           0 :             if (opt_set(UDP_OPT_IND)) {
     906           0 :                 if (!opt_set(SPORT_OPT_IND) || !opt_set(DPORT_OPT_IND))
     907           0 :                     flags |= NH_FLAG_TUNNEL_UDP_MPLS;
     908             :                 else
     909           0 :                     flags |= NH_FLAG_TUNNEL_UDP;
     910           0 :             } else if (opt_set(VXLAN_OPT_IND)) {
     911           0 :                 flags |= NH_FLAG_TUNNEL_VXLAN;
     912           0 :                 if (!opt_set(SPORT_OPT_IND) || !opt_set(DPORT_OPT_IND))
     913           0 :                     cmd_usage();
     914           0 :                 if (opt_set(L3_VXLAN_OPT_IND))
     915           0 :                     flags |= NH_FLAG_L3_VXLAN;
     916             :             }
     917             : 
     918           0 :             if (!(flags & (NH_FLAG_TUNNEL_UDP_MPLS | NH_FLAG_TUNNEL_UDP |
     919             :                         NH_FLAG_TUNNEL_VXLAN | NH_FLAG_TUNNEL_PBB)))
     920           0 :                 flags |= NH_FLAG_TUNNEL_GRE;
     921             : 
     922           0 :             if (memcmp(opt, zero_opt, sizeof(opt)))
     923           0 :                 cmd_usage();
     924           0 :         } else if (type == NH_RESOLVE) {
     925           0 :             if (memcmp(opt, zero_opt, sizeof(opt)))
     926           0 :                 cmd_usage();
     927           0 :         } else if (type == NH_DISCARD) {
     928           0 :             if (memcmp(opt, zero_opt, sizeof(opt)))
     929           0 :                 cmd_usage();
     930           0 :         } else if (type == NH_COMPOSITE) {
     931           0 :             if (!opt_set(CNI_OPT_IND))
     932           0 :                 cmd_usage();
     933             : 
     934           0 :             if (opt_set(CMC_OPT_IND)) {
     935           0 :                 flags |= NH_FLAG_MCAST;
     936             :             }
     937             : 
     938           0 :             if (opt_set(CFA_OPT_IND))
     939           0 :                 flags |= NH_FLAG_COMPOSITE_FABRIC;
     940             : 
     941           0 :             if (opt_set(CEN_OPT_IND))
     942           0 :                 flags |= NH_FLAG_COMPOSITE_ENCAP;
     943             : 
     944           0 :             if (opt_set(CEVPN_OPT_IND)) {
     945           0 :                 flags |= NH_FLAG_COMPOSITE_EVPN;
     946             : 
     947           0 :                 if (!opt_set(LBL_OPT_IND))
     948           0 :                     cmd_usage();
     949             :             }
     950             : 
     951           0 :             if (opt_set(TOR_OPT_IND)) {
     952           0 :                 flags |= NH_FLAG_COMPOSITE_TOR;
     953             : 
     954           0 :                 if (!opt_set(LBL_OPT_IND))
     955           0 :                     cmd_usage();
     956             :             }
     957             : 
     958           0 :             if (opt_set(ECMP_OPT_IND)) {
     959           0 :                 flags |= NH_FLAG_COMPOSITE_ECMP;
     960             :             }
     961             : 
     962           0 :             if (memcmp(opt, zero_opt, sizeof(opt)))
     963           0 :                 cmd_usage();
     964             : 
     965           0 :         } else if (type == NH_VRF_TRANSLATE) {
     966           0 :             if (opt_set(UUCF_OPT_IND))
     967           0 :                 flags |= NH_FLAG_UNKNOWN_UC_FLOOD;
     968             :         } else {
     969           0 :             cmd_usage();
     970             :         }
     971             : 
     972           0 :         break;
     973             : 
     974           0 :     case SANDESH_OP_DEL:
     975           0 :         if (!nh_set)
     976           0 :             cmd_usage();
     977             : 
     978           0 :         if (memcmp(opt, zero_opt, sizeof(opt)))
     979           0 :             cmd_usage();
     980           0 :         break;
     981             : 
     982           4 :     case SANDESH_OP_DUMP:
     983             :     case SANDESH_OP_GET:
     984           4 :         if (memcmp(opt, zero_opt, sizeof(opt)))
     985           0 :             usage();
     986           4 :         break;
     987             :     }
     988             : 
     989           4 :     return;
     990             : }
     991             : 
     992             : 
     993             : int
     994           4 : main(int argc, char *argv[])
     995             : {
     996             :     int opt, ind;
     997             : 
     998           4 :     nh_fill_nl_callbacks();
     999             : 
    1000          12 :     while ((opt = getopt_long(argc, argv, "",
    1001          12 :                     long_options, &ind)) >= 0) {
    1002           8 :         switch (opt) {
    1003           8 :         case 0:
    1004           8 :             parse_long_opts(ind, optarg);
    1005           8 :             break;
    1006             : 
    1007           0 :         default:
    1008           0 :             usage();
    1009             :         }
    1010             :     }
    1011             : 
    1012           4 :     if (opt_set(SOCK_DIR_OPT_IND)) {
    1013           4 :         set_platform_vtest();
    1014             :     }
    1015           4 :     validate_options();
    1016             : 
    1017           4 :     cl = vr_get_nl_client(VR_NETLINK_PROTO_DEFAULT);
    1018           4 :     if (!cl) {
    1019           0 :         exit(1);
    1020             :     }
    1021             : 
    1022           4 :     vr_nh_op(cl, command, type, nh_id, if_id, vrf_id, dst_mac,
    1023             :             src_mac, sip, dip, flags);
    1024             : 
    1025           4 :     return 0;
    1026             : }

Generated by: LCOV version 1.14