/********************************************************************
* 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 */
Exemple #2
0
/********************************************************************
* 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 */
Exemple #3
0
/********************************************************************
* 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);
            }
        }
    }
Exemple #4
0
/********************************************************************
* 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 */