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

Generated by: LCOV version 1.14