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]));
}
}
}