Access and Edit Device Configurations Using Junos XML Protocol C Client Applications
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
//--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(®ex_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])); } } }