Help us improve your experience.

Let us know what you think.

Do you have time for a two-minute survey?

Announcement: Try the Ask AI chatbot for answers to your technical questions about Juniper products and solutions.

close
external-header-nav
keyboard_arrow_up
close
keyboard_arrow_left
list Table of Contents
file_download PDF
{ "lLangCode": "en", "lName": "English", "lCountryCode": "us", "transcode": "en_US" }
English
keyboard_arrow_right

Access and Edit Device Configurations Using Junos XML Protocol C Client Applications

date_range 19-Jan-22

This example script presents a C client application that can be used to access, edit, and commit configurations on routers, switches, and security devices running Junos OS

content_copy zoom_out_map
//--Includes--//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>

//--Defines--//
//#define PRINT
     //--Toggles printing of all data to and from js server--// 

//--Global Variables and Initialization--//
int sockfd; 
char *xmlns_start_ptr = NULL;
char *xmlns_end_ptr = NULL;
int sock_bytes, pim_output_len, igmp_output_len, count_a, count_x, count_y, 
          count_z, repl_str_len, orig_len, up_to_len, remain_len, conf_chg; 
struct sockaddr_in serv_addr;
struct hostent *server;
char temp_buff[1024];           //--Temporary buffer used when --//
                                //--sending js configuration commands--//
char rcvbuffer[255];            //--Stores data from socket--//
char *pim_output_ptr = NULL;    //--Pointer for pim_output from socket--//
                                //--buffer--//
char *igmp_output_ptr = NULL;   //--Pointer for igmp_output from socket buffer--//
char small_buff[2048];          //--Buffer to support js communication--//
char jserver[16];               //--Junos XML protocol server IP address--//
int jport = 3221;               //--Junos XML protocol server port --//
                                //--(xnm-clear-text)--//
char msource[16];               //--Multicast source of group being
                                //--configured under igmp--//
char minterface[16];            //--Local multicast source interface--//
                                //--###change in igmp_xpath_ptr as well###--//
xmlDocPtr doc;                  //--Pointer struct for parsing XML--//
xmlChar *pim_xpath_ptr = 
          (xmlChar*) "/rpc-reply/pim-join-information/join-family
           /join-group[upstream-state-flags/local-source]
           /multicast-group-address";
xmlChar *temp_xpath_ptr = 
          (xmlChar*) "/rpc-reply/igmp-group-information
          /mgm-interface-groups/mgm-group
          [../interface-name = '%s']/multicast-group-address";
xmlChar *igmp_xpath_ptr = NULL;
xmlNodeSetPtr nodeset;
xmlXPathObjectPtr pim_result;   //--Pointer for pim result xml parsing--//
xmlXPathObjectPtr igmp_result;  //--Pointer for igmp result xml parsing--//
xmlChar *keyword_ptr = NULL;    //--Pointer for node text--//
char pim_result_buff[128][64];  //--Char array to store pim xPath results--//
char igmp_result_buff[128][64]; //--Char array to store igmp xPath results--//

//--js commands-//
char js_handshake1[64] = "<?xml version=\"1.0\" encoding=\"us-ascii\"?>\n";
char js_handshake2[128] = "<junoscript version=\"1.0\" 
          hostname=\"client1\" release=\"8.4R1\">\n";
char js_login[512] = "<rpc>\n<request-login>\n<username>lab</username>
          \n<challenge-response>Lablab</challenge-response>
          \n</request-login>\n</rpc>\n";
char js_show_pim[512] = "<rpc>\n<get-pim-join-information>
          \n<extensive/></get-pim-join-information></rpc>\n";
char js_show_igmp[512] = "<rpc>\n<get-igmp-group-information/>\n</rpc>\n";  
char js_rmv_group[512] = "<rpc>\n<load-configuration>\n<configuration>
          \n<protocols>\n<igmp>\n<interface>\n<name>%s</name>
          \n<static>\n<group delete='delete'>\n<name>%s</name>
          \n</group>\n</static>\n</interface>\n</igmp>\n</protocols>
          \n</configuration>\n</load-configuration>\n</rpc>\n\n\n\n\n";
char js_add_group[512] = "<rpc>\n<load-configuration>
          \n<configuration>\n<protocols>\n<igmp>
          \n<interface>\n<name>%s</name>\n<static>
          \n<group>\n<name>%s</name>\n<source>
          \n<name>%s</name>\n</source>\n</group>\n</static>
          \n</interface>\n</igmp>\n</protocols>\n</configuration>
          \n</load-configuration>\n</rpc>\n";
char js_commit[64] = "<rpc>\n<commit-configuration/>\n</rpc>\n";

//--Function prototypes--//
void error(char *msg);          //--Support error messaging--//
xmlDocPtr getdoc(char *buffer); //--Parses XML content and loads it into memory--//
xmlXPathObjectPtr getnodeset (xmlDocPtr doc, xmlChar *xpath);
          //--Parses xml content for result node(s) from XPath search--//

//--Functions--//
void error(char *msg) {
  perror(msg);
  exit(0);
}

xmlDocPtr getdoc(char *buffer) {

  xmlDocPtr doc;
  
  doc = xmlReadMemory(buffer, strlen((char *)buffer), "temp.xml", NULL, 0); 
    if (doc == NULL ) {
      fprintf(stderr,"Document not parsed successfully. \n");
      return NULL;
    } else {
      #ifdef PRINT
      printf("Document parsed successfully. \n");
      #endif
    }
    return doc;
  }

  xmlXPathObjectPtr getnodeset (xmlDocPtr doc, xmlChar *xpath) {

    xmlXPathContextPtr context;
    xmlXPathObjectPtr result;

    context = xmlXPathNewContext(doc);
    if (context == NULL) {
      printf("Error in xmlXPathNewContext\n");
      return NULL;
    }
    result = xmlXPathEvalExpression(xpath, context);
    xmlXPathFreeContext(context);
    if (result == NULL) {
      printf("Error in xmlXPathEvalExpression\n");
      return NULL;
    }
    if(xmlXPathNodeSetIsEmpty(result->nodesetval)) {
      xmlXPathFreeObject(result);
      #ifdef PRINT
      printf("No result\n");
      #endif
      return NULL;
    }
    return result;
  }

//--Main--//
  int main(int argc, char **argv) {
  
    if(argc != 4) {
      printf("\nUsage: %s <device Address> <Interface Name> 
              <Multicast Source>\n\n", argv[0]);
      exit(0);
    } else {
      strcpy(jserver, argv[1]);
      strcpy(minterface, argv[2]);
      strcpy(msource, argv[3]);
    }
    igmp_xpath_ptr = (xmlChar *)realloc((xmlChar *)igmp_xpath_ptr, 1024);  
    sprintf(igmp_xpath_ptr, temp_xpath_ptr, minterface);
  
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    server = gethostbyname(jserver);
    bzero((char*) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char*) server->h_addr, (char*) 
              &serv_addr.sin_addr.sin_addr, server->h_length);
    serv_addr.sin_port = htons(jport);

    //--Connect to the js server--//
    if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
      printf("Socket connect error\n");
    }

    if(fcntl(sockfd, F_SETOWN, getpid()) < 0)
    error("Unable to set process owner to us\n");
    printf("\nConnected to %s on port %d\n", jserver, jport);
  
  //--Read data from the initial connect--//
    sock_bytes = read(sockfd, rcvbuffer, 255);
    #ifdef PRINT
    printf("\n%s", rcvbuffer);
    #endif

  //--js intialization handshake--//
    sock_bytes = write(sockfd, js_handshake1, strlen(js_handshake1));
              //--Send xml PI to js server--//
    sock_bytes = write(sockfd, js_handshake2, strlen(js_handshake2));
              //--Send xml version and encoding to js server--//
    sock_bytes = read(sockfd, rcvbuffer, 255);
              //--Read return data from sock buffer--//
    rcvbuffer[sock_bytes] = 0;
    printf("XML connection to the Junos XML protocol server has been initialized\n");
    #ifdef PRINT
    printf("\n%s", rcvbuffer);
    #endif

  //--js login--//
    sock_bytes = write(sockfd, js_login, strlen(js_login));
              //--Send js command--//
    while(strstr(small_buff, "superuser") == NULL) {
              //--Continue to read from the buffer until match--//
      sock_bytes = read(sockfd, rcvbuffer, 255);
      rcvbuffer[sock_bytes] = 0;              
      strcat(small_buff, rcvbuffer);
                //--Copy buffer contents into pim_buffer--//
    }
    printf("Login completed to the Junos XML protocol server\n");
    #ifdef PRINT
    printf("%s\n", small_buff);  //--Print the small buff contents--//
    #endif
  //regfree(&regex_struct);
    bzero(small_buff, strlen(small_buff));
              //--Erase small buffer contents--//
  
  //--Begin the for loop here--//
    printf("Running continuous IGMP and PIM group comparison...\n\n");
    for(;;) {                   //--Begin infinite for loop--//
  
  //--Get PIM join information--//
      pim_output_ptr = (char *)realloc((char *)pim_output_ptr, 
                strlen(js_handshake1));
                //--Allocate memory for xml PI concatenation --//
                //--to pim_output_ptr--//
      strcpy(pim_output_ptr, js_handshake1);
                //--Copy PI to pim_output_ptr--//
      sock_bytes = write(sockfd, js_show_pim, strlen(js_show_pim));
                //--Send show pim joins command--//
      while(strstr(pim_output_ptr, "</rpc-reply>") == NULL) {
                //--Continue to read from the buffer until match--//
        sock_bytes = read(sockfd, rcvbuffer, 255);
                  //--Read from buffer--//
        rcvbuffer[sock_bytes] = 0;                
        pim_output_len = strlen((char *)pim_output_ptr);
                  //--Determine current string length of pim_output_ptr--//
        pim_output_ptr = (char *)realloc((char *)pim_output_ptr, 
                  strlen(rcvbuffer)+pim_output_len);
                  //--Reallocate memory for additional data--//
        strcat(pim_output_ptr, rcvbuffer);
                  //--Copy data from rcvbuffer to pim_output_ptr--//
      }
    
  //--Remove the xmlns entry--//
      xmlns_start_ptr = strstr(pim_output_ptr, "xmlns=\"http:");
                //--Find the start of the xmlns entry--pointer --//
                //--returned by strstr()--//
      xmlns_end_ptr = strstr(xmlns_start_ptr, ">");
                //--Find the end of the xmlns entry--pointer --//
                //--returned by strstr()--//
      repl_str_len = xmlns_end_ptr - xmlns_start_ptr;
                //--Determine the length of the string to be replaced--//
      orig_len = strlen((char *)pim_output_ptr) + 1;
                //--Determine the original length of pim_output--//  
      up_to_len = xmlns_start_ptr - pim_output_ptr;
                //--Determine the length up to the beginning --//
                //--of the xmlns entry--//
      remain_len = orig_len - (up_to_len + repl_str_len);
                //--Determine what the remaining length is minus --//
                //--what we are removing--//
      memcpy(xmlns_start_ptr - 1, xmlns_start_ptr + repl_str_len, remain_len);
                //--copy the remaining string to the beginning --//
                //--of the replacement string--//
      #ifdef PRINT 
      printf("\n%s\n", pim_output_ptr);
      #endif
  //--End of GET PIM join information--//
  
  //--Get IGMP membership information--//
      igmp_output_ptr = (char *)realloc((char *)igmp_output_ptr, 
                strlen(js_handshake1));
      strcpy(igmp_output_ptr, js_handshake1);
      sock_bytes = write(sockfd, js_show_igmp, strlen(js_show_igmp));
      while(strstr(igmp_output_ptr, "</rpc-reply>") == NULL) {
        sock_bytes = read(sockfd, rcvbuffer, 255);
        rcvbuffer[sock_bytes] = 0;
        igmp_output_len = strlen((char *)igmp_output_ptr);
        igmp_output_ptr = (char *)realloc((char *)igmp_output_ptr, 
                  strlen(rcvbuffer)+igmp_output_len);
        strcat(igmp_output_ptr, rcvbuffer);
      }
      #ifdef PRINT
      printf("\n%s\n", igmp_output_ptr);
      #endif
  //--End of GET IGMP membership information--//

  //--Store xPath results for pim buffer search--//
      doc = getdoc(pim_output_ptr); 
                //--Call getdoc() to parse XML in pim_output--//
      pim_result = getnodeset (doc, pim_xpath_ptr);
                //--Call getnodeset() which provides xPath result--//
      if (pim_result) {
        nodeset = pim_result->nodesetval;
        for (count_a=0; count_a < nodeset->nodeNr; count_a++) {
                  //--Run through all node values found--//
          keyword_ptr = xmlNodeListGetString
                    (doc, nodeset->nodeTab[count_a]->xmlChildrenNode, 1);
          strcpy(pim_result_buff[count_a], (char *)keyword_ptr);
                    //--Copy each node value to its own array element--//
        #ifdef PRINT
        printf("PIM Groups: %s\n", pim_result_buff[count_a]);
                  //--Print the node value--//
        #endif
      
        xmlFree(keyword_ptr);     //--Free memory used by keyword_ptr--//
        xmlChar *keyword_ptr = NULL;
        }
      xmlXPathFreeObject(pim_result);
                //--Free memory used by result--//
    }
    xmlFreeDoc(doc);              //--Free memory used by doc--//
    xmlCleanupParser();           //--Clean everything else--//
  //--End of xPath search--//

  //--Store xPath results for igmp buffer search--//
    doc = getdoc(igmp_output_ptr);
    igmp_result = getnodeset (doc, igmp_xpath_ptr);
    if (igmp_result) {
      nodeset = igmp_result->nodesetval;
      for (count_a=0; count_a < nodeset->nodeNr; count_a++) {
        keyword_ptr = xmlNodeListGetString
                  (doc, nodeset->nodeTab[count_a]->xmlChildrenNode, 1);
        strcpy(igmp_result_buff[count_a], (char *)keyword_ptr);
        #ifdef PRINT
        printf("IGMP Groups: %s\n", igmp_result_buff[count_a]);
        #endif
      
        xmlFree(keyword_ptr);
        xmlChar *keyword_ptr = NULL;
      }
      xmlXPathFreeObject(igmp_result);
    }
    xmlFreeDoc(doc);
    xmlCleanupParser();
  //--End of xPath search--//
  
  //--Code to compare pim groups to configured igmp static membership--//
    conf_chg = 0;
    count_x=0;                   //--Track pim groups--//
    count_y=0;                   //--Track igmp groups--//
    count_z=0;          //--Track matches (if set to 1, igmp group matched pim group)--//
    while(strstr(pim_result_buff[count_x], "2") != NULL) {
              //--Run through igmp pim groups--//
      if(strstr(igmp_result_buff[count_y], "2") == NULL) {
        count_z = 0;
        conf_chg = 1;
      }
      while(strstr(igmp_result_buff[count_y], "2") != NULL) {
               //--For each pim group, run through all igmp groups--//
        if(strcmp(igmp_result_buff[count_y], pim_result_buff[count_x]) == 0) {
                  //--If igmp group matches pim group, set z to 1 --//
                  //-- (ie  count_z=1; --//
                  //--Set z to 1 if there was a match (ie - the static --//
                  //--membership is configured)--//
        }
        count_y++;              //--Increment igmp result buffer--//
      }
      if(count_z == 0) {        //--If no igmp group matched the --//
                                //--pim group (z stayed at 0), configure--// 
                                //--static membership--//
        printf("Adding this group to igmp: %s\n", pim_result_buff[count_x]);
        sprintf(temp_buff, js_add_group, minterface, 
                pim_result_buff[count_x], msource);  
                //--Copy js_add_group with pim group to temp_buff--//
        #ifdef PRINT
        printf("%s", temp_buff);
        #endif
        sock_bytes = write(sockfd, temp_buff, strlen(temp_buff));               
        while(strstr(small_buff, "</rpc-reply>") == NULL) {
          sock_bytes = read(sockfd, rcvbuffer, 255);
          rcvbuffer[sock_bytes] = 0;                                      
          strcat(small_buff, rcvbuffer);                                 
        }
        #ifdef PRINT
        printf("%s\n", small_buff);
        #endif
        bzero(small_buff, strlen(small_buff));
                  //--Erase (copy all 0's) small buffer contents--//
        bzero(temp_buff, strlen(temp_buff));
                  //--Erase temp_buff contents--//
        conf_chg = 1; 
                  //--Set conf_chg value to 1 to signify that a --//
                  //--commit is needed--//
      }
      count_x++;                //--increment pim result buffer--//
      count_y=0;                //--reset igmp result buffer to start--//
                                //-- at first element--//
      count_z=0;                //--reset group match to 0 --//
                                //--(config needed due to no match)--/
    }  
  
  //--Code for comparing igmp static membership to pim groups--//
    count_x=0;                       
    count_y=0;                         
    count_z=0;                         
    while(strstr(igmp_result_buff[count_y], "2") != NULL) {            
      if(strstr(pim_result_buff[count_x], "2") == NULL) {  
        count_z = 0;
        conf_chg = 1;
      }
      while(strstr(pim_result_buff[count_x], "2") != NULL) {        
        if(strcmp(pim_result_buff[count_x], igmp_result_buff[count_y]) == 0) {     
          count_z = 1;                 
        }
        count_x++;                
      }
      if(count_z == 0) {              
        printf("Removing this group from igmp: %s\n", igmp_result_buff[count_y]);
        sprintf(temp_buff, js_rmv_group, minterface, igmp_result_buff[count_y]);
        #ifdef PRINT
        printf("%s", temp_buff);
        #endif
        sock_bytes = write(sockfd, temp_buff, strlen(temp_buff));                                          
        while(strstr(small_buff, "</rpc-reply>") == NULL) {
          sock_bytes = read(sockfd, rcvbuffer, 255);
          rcvbuffer[sock_bytes] = 0;                                                     
          strcat(small_buff, rcvbuffer);                                                
        }
        #ifdef PRINT
        printf("%s\n", rcvbuffer);
        #endif
        bzero(small_buff, strlen(small_buff));            
        bzero(temp_buff, strlen(temp_buff));
        conf_chg = 1;
      }
      count_y++;                  
      count_x=0;                
      count_z=0;              
    }  
  
    if(conf_chg == 1) {
      sock_bytes = write(sockfd, js_commit, strlen(js_commit));      
      while(strstr(small_buff, "</rpc-reply>") == NULL) {
        sock_bytes = read(sockfd, rcvbuffer, 255);
        rcvbuffer[sock_bytes] = 0;                                           
        strcat(small_buff, rcvbuffer);                                      
      }
      bzero(small_buff, strlen(small_buff));          
      printf("\nCommitted configuration change\n");
    } else {
      #ifdef PRINT
      printf("\nNo configuration changes made\n");
      #endif
    }
    #ifdef PRINT
    printf("\n%s\n", small_buff);
    #endif
    
  //--Cleanup before next round of checks--//
    bzero(rcvbuffer, strlen(rcvbuffer));
              //--Erase contents of rcvbuffer--//
    char *xmlns_start_ptr = NULL;
              //--Nullify the contents--//
    char *xmlns_end_ptr = NULL;
              //--Nullify the contents--//
    for(count_x = 0; count_x < 129; count_x++) {
              //--Erase contents of both pim_result_buff and igmp_result_buff--//
      bzero(pim_result_buff[count_x], strlen(pim_result_buff[count_x]));
      bzero(igmp_result_buff[count_x], strlen(igmp_result_buff[count_x]));
    }
  }
}  
external-footer-nav