/******************************************************************** * FUNCTION check_manager_hello * * Verify that the same NETCONF protocol verion is supported * by the manager and this server * * INPUTS: * scb == session control block * val == value struct for the hello message to check * * RETURNS: * status *********************************************************************/ static status_t check_manager_hello (ses_cb_t *scb, val_value_t *val) { val_value_t *caps, *cap; /* look for the NETCONF base capability string */ caps = val_find_child(val, NC_MODULE, NCX_EL_CAPABILITIES); if (caps && caps->res == NO_ERR) { if (ncx_protocol_enabled(NCX_PROTO_NETCONF11)) { for (cap = val_find_child(caps, NC_MODULE, NCX_EL_CAPABILITY); cap != NULL; cap = val_find_next_child(caps, NC_MODULE, NCX_EL_CAPABILITY, cap)) { if (cap->res == NO_ERR) { if (!xml_strcmp(VAL_STR(cap), CAP_BASE_URN11)) { if (LOGDEBUG3) { log_debug3("\nagt_hello: set " "protocol to base:1.1"); } return ses_set_protocol(scb, NCX_PROTO_NETCONF11); } } } } if (ncx_protocol_enabled(NCX_PROTO_NETCONF10)) { for (cap = val_find_child(caps, NC_MODULE, NCX_EL_CAPABILITY); cap != NULL; cap = val_find_next_child(caps, NC_MODULE, NCX_EL_CAPABILITY, cap)) { if (cap->res == NO_ERR) { if (!xml_strcmp(VAL_STR(cap), CAP_BASE_URN)) { if (LOGDEBUG3) { log_debug3("\nagt_hello: set " "protocol to base:1.0"); } return ses_set_protocol(scb, NCX_PROTO_NETCONF10); } } } } } log_info("\nagt_hello: no NETCONF base:1.0 or base:1.1 URI found"); return ERR_NCX_MISSING_VAL_INST; } /* check_manager_hello */
/******************************************************************** * FUNCTION process_server_hello * * Process the NETCONF server <hello> contents * * 1) Protocol capabilities * 2) Module capabilities * 3) Unrecognized capabilities * * INPUTS: * scb == session control block to set * hello == value struct for the hello message to check * * OUTPUTS: * server caps in the scb->mgrcb is set * * RETURNS: * status *********************************************************************/ static status_t process_server_hello (ses_cb_t *scb, val_value_t *hello) { val_value_t *caps, *sidval, *cap; mgr_scb_t *mscb; boolean c1, c2; status_t res; mscb = mgr_ses_get_mscb(scb); /* make sure the capabilities element is present * This should not fail, since already parsed this far */ caps = val_find_child(hello, NC_MODULE, NCX_EL_CAPABILITIES); if (!caps || caps->res != NO_ERR) { log_error("\nError: no <capabilities> found in server <hello>"); return ERR_NCX_MISSING_VAL_INST; } /* make sure the session-id element is present * This should not fail, since already parsed this far */ sidval = val_find_child(hello, NC_MODULE, NCX_EL_SESSION_ID); if (!sidval || sidval->res != NO_ERR) { log_error("\nError: no <session-id> found in server <hello>"); return ERR_NCX_MISSING_VAL_INST; } else { mscb->agtsid = VAL_UINT(sidval); } /* go through the capability nodes and construct a caplist */ for (cap = val_find_child(caps, NC_MODULE, NCX_EL_CAPABILITY); cap != NULL; cap = val_find_next_child(caps, NC_MODULE, NCX_EL_CAPABILITY, cap)) { if (cap->res != NO_ERR) { continue; } res = cap_add_std_string(&mscb->caplist, VAL_STR(cap)); if (res == ERR_NCX_SKIPPED) { res = cap_add_module_string(&mscb->caplist, VAL_STR(cap)); if (res == ERR_NCX_SKIPPED) { /* * if (ncx_warning_enabled(ERR_NCX_RCV_UNKNOWN_CAP)) { * log_warn("\nWarning: received unknown capability '%s'", * VAL_STR(cap)); * } */ if (LOGDEBUG2) { log_debug2("\nmgr: Got enterprise capability %s", VAL_STR(cap)); } /* hack: check for juniper 1.0 server * change the useprefix mode to TRUE to get * <rpc> operations to work with this server */ if (!xml_strcmp(VAL_STR(cap), CAP_JUNOS)) { if (LOGDEBUG) { log_debug("\nUsing XML prefixes to work " "with Junos 1.0 server\n"); } ncx_set_useprefix(TRUE); } res = cap_add_ent(&mscb->caplist, VAL_STR(cap)); if (res != NO_ERR) { return res; } } } } /* check if the mandatory base protocol capability was set */ res = NO_ERR; c1 = cap_std_set(&mscb->caplist, CAP_STDID_V1); c2 = cap_std_set(&mscb->caplist, CAP_STDID_V11); if (c1 && c2) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: server supports " "base:1.0 and base:1.1"); } if (ses_protocol_requested(scb, NCX_PROTO_NETCONF11)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.1 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF11); } else if (ses_protocol_requested(scb, NCX_PROTO_NETCONF10)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.0 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF10); } else { log_error("\nError: Internal: no protocols requested, " "dropping session '%d'", scb->sid); res = ERR_NCX_MISSING_VAL_INST; } } else if (c1) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: server supports " "base:1.0 only"); } if (ses_protocol_requested(scb, NCX_PROTO_NETCONF10)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.0 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF10); } else { log_error("\nError: Server supports base:1.0 only;" "\n Protocol 'netconf1.0' not enabled, " "dropping session '%d'", scb->sid); res = ERR_NCX_MISSING_VAL_INST; } } else if (c2) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: server supports " "base:1.1 only"); } if (ses_protocol_requested(scb, NCX_PROTO_NETCONF11)) { if (LOGDEBUG2) { log_debug2("\nmgr_hello: set protocol to base:1.1 " "for session '%d'", scb->sid); } ses_set_protocol(scb, NCX_PROTO_NETCONF11); } else { log_error("\nError: Server supports base:1.1 only;" "\n Protocol 'netconf1.1' not enabled, " "dropping session '%d'", scb->sid); res = ERR_NCX_MISSING_VAL_INST; } } else { log_error("\nError: no support for base:1.0 " "or base:1.1 found in server <hello>;" "\n dropping session '%d'", scb->sid); return ERR_NCX_MISSING_VAL_INST; } /* set target type var in the manager session control block */ c1 = cap_std_set(&mscb->caplist, CAP_STDID_WRITE_RUNNING); c2 = cap_std_set(&mscb->caplist, CAP_STDID_CANDIDATE); if (c1 && c2) { mscb->targtyp = NCX_AGT_TARG_CAND_RUNNING; } else if (c1) { mscb->targtyp = NCX_AGT_TARG_RUNNING; } else if (c2) { mscb->targtyp = NCX_AGT_TARG_CANDIDATE; } else { mscb->targtyp = NCX_AGT_TARG_NONE; if (LOGINFO) { log_info("\nmgr_hello: no writable target found for" " session %u (a:%u)", scb->sid, mscb->agtsid); } } /* set the startup type in the mscb */ if (cap_std_set(&mscb->caplist, CAP_STDID_STARTUP)) { mscb->starttyp = NCX_AGT_START_DISTINCT; } else { mscb->starttyp = NCX_AGT_START_MIRROR; } return NO_ERR; } /* process_server_hello */
/******************************************************************** * FUNCTION agt_init2 * * Initialize the Server Library * The agt_profile is set and the object database is * ready to have YANG modules loaded * * RPC and data node callbacks should be installed * after the module is loaded, and before the running config * is loaded. * * RETURNS: * status *********************************************************************/ status_t agt_init2 (void) { val_value_t *clivalset; ncx_module_t *retmod; val_value_t *val; agt_dynlib_cb_t *dynlib; xmlChar *savestr, *revision, savechar; cfg_template_t *cfg; status_t res; uint32 modlen; boolean startup_loaded; log_debug3("\nServer Init-2 Starting..."); startup_loaded = FALSE; /* init user callback support */ agt_cb_init(); agt_commit_complete_init(); agt_commit_validate_init(); agt_not_queue_notification_cb_init(); /* initial signal handler first to allow clean exit */ agt_signal_init(); /* initialize the server timer service */ agt_timer_init(); /* initialize the RPC server callback structures */ res = agt_rpc_init(); if (res != NO_ERR) { log_debug3("\nError in rpc_init"); return res; } /* initialize the NCX connect handler */ res = agt_connect_init(); if (res != NO_ERR) { log_debug3("\nError in agt_connect_init"); return res; } /* initialize the NCX hello handler */ res = agt_hello_init(); if (res != NO_ERR) { log_debug3("\nError in agt_hello_init"); return res; } /* setup an empty <running> config * The config state is still CFG_ST_INIT * so no user access can occur yet (except OP_LOAD by root) */ res = cfg_init_static_db(NCX_CFGID_RUNNING); if (res != NO_ERR) { return res; } /* set the 'ordered-by system' sorted/not-sorted flag */ ncx_set_system_sorted(agt_profile.agt_system_sorted); /* set the 'top-level mandatory objects allowed' flag */ ncx_set_top_mandatory_allowed(!agt_profile.agt_running_error); /*** All Server profile parameters should be set by now ***/ /* must set the server capabilities after the profile is set */ res = agt_cap_set_caps(agt_profile.agt_targ, agt_profile.agt_start, agt_profile.agt_defaultStyle); if (res != NO_ERR) { return res; } /* setup the candidate config if it is used */ if (agt_profile.agt_targ==NCX_AGT_TARG_CANDIDATE) { res = cfg_init_static_db(NCX_CFGID_CANDIDATE); if (res != NO_ERR) { return res; } } /* setup the startup config if it is used */ if (agt_profile.agt_start==NCX_AGT_START_DISTINCT) { res = cfg_init_static_db(NCX_CFGID_STARTUP); if (res != NO_ERR) { return res; } } /* initialize the server access control model */ res = agt_acm_init(); if (res != NO_ERR) { return res; } /* initialize the session handler data structures */ agt_ses_init(); /* load the system module */ res = agt_sys_init(); if (res != NO_ERR) { return res; } /* load the NETCONF state monitoring data model module */ res = agt_state_init(); if (res != NO_ERR) { return res; } /* load the NETCONF Notifications data model module */ res = agt_not_init(); if (res != NO_ERR) { return res; } /* load the NETCONF /proc monitoring data model module */ res = agt_proc_init(); if (res != NO_ERR) { return res; } /* load the partial lock module */ res = y_ietf_netconf_partial_lock_init (y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, NULL); if (res != NO_ERR) { return res; } #if 0 // OpenClovis: we do not want this by default /* load the NETCONF interface monitoring data model module */ res = agt_if_init(); if (res != NO_ERR) { return res; } #endif /* initialize the NCX server core callback functions. * the schema (yuma-netconf.yang) for these callbacks was * already loaded in the common ncx_init * */ res = agt_ncx_init(); if (res != NO_ERR) { return res; } /* load the yuma-time-filter module */ res = y_yuma_time_filter_init (y_yuma_time_filter_M_yuma_time_filter, NULL); if (res != NO_ERR) { return res; } /* load the yuma-arp module */ res = y_yuma_arp_init(y_yuma_arp_M_yuma_arp, NULL); if (res != NO_ERR) { return res; } /* check the module parameter set from CLI or conf file * for any modules to pre-load */ res = NO_ERR; clivalset = agt_cli_get_valset(); if (clivalset) { if (LOGDEBUG) { log_debug("\n\nnetconfd final CLI + .conf parameters:\n"); val_dump_value_max(clivalset, 0, NCX_DEF_INDENT, DUMP_VAL_LOG, NCX_DISPLAY_MODE_PLAIN, FALSE, /* withmeta */ TRUE /* config only */); log_debug("\n"); } /* first check if there are any deviations to load */ val = val_find_child(clivalset, NCXMOD_NETCONFD, NCX_EL_DEVIATION); while (val) { res = ncxmod_load_deviation(VAL_STR(val), &agt_profile.agt_savedevQ); if (res != NO_ERR) { return res; } else { val = val_find_next_child(clivalset, NCXMOD_NETCONFD, NCX_EL_DEVIATION, val); } } val = val_find_child(clivalset, NCXMOD_NETCONFD, NCX_EL_MODULE); /* attempt all dynamically loaded modules */ while (val && res == NO_ERR) { /* see if the revision is present in the * module parameter or not */ modlen = 0; revision = NULL; savestr = NULL; savechar = '\0'; if (yang_split_filename(VAL_STR(val), &modlen)) { savestr = &(VAL_STR(val)[modlen]); savechar = *savestr; *savestr = '\0'; revision = savestr + 1; } #ifdef STATIC_SERVER /* load just the module * SIL initialization is assumed to be * handled elsewhere */ res = ncxmod_load_module(VAL_STR(val), revision, &agt_profile.agt_savedevQ, &retmod); } #else /* load the SIL and it will load its own module */ res = agt_load_sil_code(VAL_STR(val), revision, FALSE); if (res == ERR_NCX_SKIPPED) { log_warn("\nWarning: SIL code for module '%s' not found", VAL_STR(val)); res = ncxmod_load_module(VAL_STR(val), revision, &agt_profile.agt_savedevQ, &retmod); } #endif if (savestr != NULL) { *savestr = savechar; } if (res == NO_ERR) { val = val_find_next_child(clivalset, NCXMOD_NETCONFD, NCX_EL_MODULE, val); } } }
/******************************************************************** * FUNCTION y_ietf_netconf_partial_lock_partial_lock_validate * * RPC validation phase * All YANG constraints have passed at this point. * Add description-stmt checks in this function. * * INPUTS: * see agt/agt_rpc.h for details * * RETURNS: * error status ********************************************************************/ static status_t y_ietf_netconf_partial_lock_partial_lock_validate ( ses_cb_t *scb, rpc_msg_t *msg, xml_node_t *methnode) { plock_cb_t *plcb; cfg_template_t *running; val_value_t *select_val, *testval; xpath_result_t *result; xpath_resnode_t *resnode; status_t res, retres; ses_id_t lockowner; res = NO_ERR; retres = NO_ERR; plcb = NULL; result = NULL; running = cfg_get_config_id(NCX_CFGID_RUNNING); /* make sure the running config state is READY */ res = cfg_ok_to_partial_lock(running); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); return res; } /* the stack has already made sure at least 1 of these * is present because min-elements=1 */ select_val = val_find_child (msg->rpc_input, y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_select); if (select_val == NULL || select_val->res != NO_ERR) { /* should not happen */ return SET_ERROR(ERR_INTERNAL_VAL); } /* allocate a new lock cb * the plcb pointer will be NULL if the result is not NO_ERR */ res = NO_ERR; plcb = plock_cb_new(SES_MY_SID(scb), &res); if (res == ERR_NCX_RESOURCE_DENIED && !plcb && !cfg_is_partial_locked(running)) { /* no partial locks so it is safe to reset the lock index */ plock_cb_reset_id(); res = NO_ERR; plcb = plock_cb_new(SES_MY_SID(scb), &res); } if (res != NO_ERR || !plcb ) { if ( res==NO_ERR && !plcb ) { res = ERR_INTERNAL_MEM; } agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); if ( plcb ) { plock_cb_free(plcb); } return res; } /* get all the select parm instances and save them * in the partial lock cb */ while (select_val != NULL) { result = xpath1_eval_xmlexpr(scb->reader, select_val->xpathpcb, running->root, running->root, FALSE, /* logerrors */ TRUE, /* config-only */ &res); if (result == NULL || res != NO_ERR) { /* should not get invalid XPath expression * but maybe something was valid in the * object tree but not in the value tree */ agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, VAL_STRING(select_val), NCX_NT_VAL, select_val); retres = res; xpath_free_result(result); } else if (result->restype != XP_RT_NODESET) { res = ERR_NCX_XPATH_NOT_NODESET; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_STRING, VAL_STRING(select_val), NCX_NT_VAL, select_val); retres = res; xpath_free_result(result); } else { /* save these pointers; the result will be * pruned for redundant nodes after all the * select expressions have been processed; * transfer the memory straight across */ plock_add_select(plcb, select_val->xpathpcb, result); select_val->xpathpcb = NULL; result = NULL; } /* set up the next select value even if errors so far */ select_val = val_find_next_child (msg->rpc_input, y_ietf_netconf_partial_lock_M_ietf_netconf_partial_lock, y_ietf_netconf_partial_lock_N_select, select_val); } /* check the result for non-empty only if all * the select stmts were valid */ if (retres == NO_ERR) { res = plock_make_final_result(plcb); if (res != NO_ERR) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, select_val); retres = res; } } /* make sure all the nodeptrs identify subtrees in the * running config that can really be locked right now * only if the final result is a non-empty nodeset */ if (retres == NO_ERR) { result = plock_get_final_result(plcb); for (resnode = xpath_get_first_resnode(result); resnode != NULL; resnode = xpath_get_next_resnode(resnode)) { testval = xpath_get_resnode_valptr(resnode); /* !!! Update 2011-09-27 * Just talked to the RFC author; Martin and I * agree RFC 5717 does not specify that write * access be required to create a partial lock * In fact -- a user may want to lock a node * to do a stable read **** deleted agt_acm check ***/ /* make sure there is a plock slot available * and no part of this subtree is already locked * do not check lock conflicts if this subtree * is unauthorized for writing */ lockowner = 0; res = val_ok_to_partial_lock(testval, SES_MY_SID(scb), &lockowner); if (res != NO_ERR) { if (res == ERR_NCX_LOCK_DENIED) { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_UINT32_PTR, &lockowner, NCX_NT_VAL, testval); } else { agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_VAL, testval); } retres = res; } } // for loop } // if retres == NO_ERR /* make sure there is no partial commit in progress */ if (agt_ncx_cc_active()) { res = ERR_NCX_IN_USE_COMMIT; agt_record_error(scb, &msg->mhdr, NCX_LAYER_OPERATION, res, methnode, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); retres = res; } /* save the plock CB only if there were no errors; * otherwise the invoke function will not get called */ if (retres == NO_ERR) { /* the invoke function must free this pointer */ msg->rpc_user1 = plcb; } else { plock_cb_free(plcb); } return retres; } /* y_ietf_netconf_partial_lock_partial_lock_validate */