/**
 * context: {node}
 * tokens: {address|prefix, type}
 * type: {rtr, ptp, ptmp, virtual}
 */
static int cli_net_node_add_iface(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  net_node_t * node= _node_from_context(ctx);
  const char * arg_if= cli_get_arg_value(cmd, 0);
  const char * arg_type= cli_get_arg_value(cmd, 1);
  net_error_t error;
  net_iface_id_t iface;
  net_iface_type_t type;

  // Get interface identifier
  error= net_iface_str2id(arg_if, &iface);
  if (error != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not add interface (%s)",
		       network_strerror(error));
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Get interface type
  error= net_iface_str2type(arg_type, &type);
  if (error != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not add interface (%s)",
		       network_strerror(error));
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Add new interface
  error= node_add_iface(node, iface, type);
  if (error != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not add interface (%s)",
		       network_strerror(error));
    return CLI_ERROR_COMMAND_FAILED;
  }

  return CLI_SUCCESS;
}
Exemple #2
0
/**
 * Updates the error status of the given subscription
 *
 * @param subscription	the subscription
 * @param httpstatus	the new HTTP status code
 * @param resultcode	the update result code
 * @param filterError	filter error string (or NULL)
 */
static void
subscription_update_error_status (subscriptionPtr subscription,
                                  gint httpstatus,
                                  gint resultcode,
                                  gchar *filterError)
{
	gboolean	errorFound = FALSE;

	if (subscription->filterError)
		g_free (subscription->filterError);
	if (subscription->httpError)
		g_free (subscription->httpError);
	if (subscription->updateError)
		g_free (subscription->updateError);

	subscription->filterError = g_strdup (filterError);
	subscription->updateError = NULL;
	subscription->httpError = NULL;
	subscription->httpErrorCode = httpstatus;

	if (((httpstatus >= 200) && (httpstatus < 400)) && /* HTTP codes starting with 2 and 3 mean no error */
	    (NULL == subscription->filterError))
		return;

	if ((200 != httpstatus) || (resultcode != 0)) {
		subscription->httpError = g_strdup (network_strerror (resultcode, httpstatus));
		errorFound = TRUE;
	}

	/* if none of the above error descriptions matched... */
	if (!errorFound)
		subscription->updateError = g_strdup (_("There was a problem while reading this subscription. Please check the URL and console output."));
}
/**
 * context: {node}
 * tokens: {prefix|address}
 */
static int cli_ctx_create_iface(cli_ctx_t * ctx, cli_cmd_t * cmd,
				void ** item_ref)
{
  net_node_t * node= _node_from_context(ctx);
  const char * arg= cli_get_arg_value(cmd, 0);
  net_iface_t * iface= NULL;
  net_iface_id_t iface_id;
  int result;

  assert(node != NULL);
  
  // Get interface identifier
  result= net_iface_str2id(arg, &iface_id);
  if (result != ESUCCESS) {
    cli_set_user_error(cli_get(), "invalid input \"%s\" (%s)", arg,
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Find interface in current node
  iface= node_find_iface(node, iface_id);
  if (iface == NULL) {
    cli_set_user_error(cli_get(), "no such interface \"%s\".", arg);
    return CLI_ERROR_COMMAND_FAILED;
  }

  *item_ref= iface;
  return CLI_SUCCESS;
}
Exemple #4
0
/*
 * Class:     be_ac_ucl_ingi_cbgp_net_Node
 * Method:    addRoute
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)I
 */
JNIEXPORT void JNICALL Java_be_ac_ucl_ingi_cbgp_net_Node_addRoute
  (JNIEnv * jEnv, jobject joNode, jstring jsPrefix,
   jstring jsNexthop, jint jiWeight)
{
  net_node_t * node;
  ip_pfx_t prefix;
  net_addr_t next_hop;
  net_iface_id_t tIfaceID;
  int error;

  jni_lock(jEnv);

  /* Get the node */
  node= (net_node_t*) jni_proxy_lookup(jEnv, joNode);
  if (node == NULL)
    return_jni_unlock2(jEnv);

  if (ip_jstring_to_prefix(jEnv, jsPrefix, &prefix) != 0)
    return_jni_unlock2(jEnv);

  if (ip_jstring_to_address(jEnv, jsNexthop, &next_hop) != 0)
    return_jni_unlock2(jEnv);

  tIfaceID.network= next_hop;
  tIfaceID.mask= 32;
  error= node_rt_add_route(node, prefix, tIfaceID,
			   next_hop, jiWeight, NET_ROUTE_STATIC);
  if (error != ESUCCESS) {
    throw_CBGPException(jEnv, "could not add route (%s)",
			network_strerror(error));
    return_jni_unlock2(jEnv);
  }

  jni_unlock(jEnv);
}
Exemple #5
0
/**
 * Dump an error message for the given error code.
 */
void network_perror(gds_stream_t * stream, int error)
{
  const char * error_msg= network_strerror(error);
  if (error_msg != NULL)
    stream_printf(stream, error_msg);
  else
    stream_printf(stream, "unknown error (%i)", error);    
}
Exemple #6
0
/*
 * Class:     be_ac_ucl_ingi_cbgp_net_Node
 * Method:    addPTPLink
 * Signature: (Lbe/ac/ucl/ingi/cbgp/net/Node;Ljava/lang/String;Ljava/lang/String;Z)Lbe/ac/ucl/ingi/cbgp/net/Link;
 */
JNIEXPORT jobject JNICALL Java_be_ac_ucl_ingi_cbgp_net_Node_addPTPLink
  (JNIEnv * jEnv, jobject joNode, jobject joDst, jstring jsSrcIface,
   jstring jsDstIface, jboolean jbBidir)
{
  net_node_t * pNode;
  net_node_t * pNodeDst;
  int iResult;
  net_iface_t * pIface;
  net_iface_id_t tSrcIfaceID;
  net_iface_id_t tDstIfaceID;
  jobject joIface;

  jni_lock(jEnv);

  /* Get the node */
  pNode= (net_node_t*) jni_proxy_lookup(jEnv, joNode);
  if (pNode == NULL)
    return_jni_unlock(jEnv, NULL); 

  /* Get the destination */
  pNodeDst= (net_node_t*) jni_proxy_lookup(jEnv, joDst);
  if (pNodeDst == NULL)
    return_jni_unlock(jEnv, NULL); 

  /* Get the source's interface ID */
  if (ip_jstring_to_prefix(jEnv, jsSrcIface, &tSrcIfaceID) != 0)
    return_jni_unlock(jEnv, NULL);

  /* Get the destination's interface ID */
  if (ip_jstring_to_prefix(jEnv, jsDstIface, &tDstIfaceID) != 0)
    return_jni_unlock(jEnv, NULL);

  /* Add link */
  iResult= net_link_create_ptp(pNode, tSrcIfaceID,
			       pNodeDst, tDstIfaceID,
			       (jbBidir==JNI_TRUE) ? BIDIR : UNIDIR,
			       &pIface);
  if (iResult != ESUCCESS) {
    throw_CBGPException(jEnv, "could not create link (%s)",
			network_strerror(iResult));
    return_jni_unlock(jEnv, NULL);
  }

  /* Retrieve new link */
  joIface= cbgp_jni_new_net_Interface(jEnv,
				 NULL/*jni_proxy_get_CBGP(jEnv, joNode)*/,
				 pIface);

  return_jni_unlock(jEnv, joIface);
}
Exemple #7
0
/**
 * context: {}
 * tokens : {addr}
 * options: {no-loopback}
 */
int cli_net_add_node(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  net_addr_t addr;
  net_node_t * node;
  int error;
  int options= 0; // default is: create loopback with address = node ID

  // Node address ?
  if (str2address(cli_get_arg_value(cmd, 0), &addr)) {
    cli_set_user_error(cli_get(), "could not add node (invalid address)");
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Option: no-loopback ?
  if (!cli_opts_has_value(cmd->opts, "no-loopback")) {
    options|= NODE_OPTIONS_LOOPBACK;
  }

  // Create new node
  error= node_create(addr, &node, options);
  if (error != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not add node (%s)",
		       network_strerror(error));
    return CLI_ERROR_COMMAND_FAILED;    
  }

  // Add node
  error= network_add_node(network_get_default(), node);
  if (error != ESUCCESS) {
    node_destroy(&node);
    cli_set_user_error(cli_get(), "could not add node (%s)",
		       network_strerror(error));
    return CLI_ERROR_COMMAND_FAILED;
  }
  return CLI_SUCCESS;
}
Exemple #8
0
/**
 * context: {}
 * tokens: {prefix, type}
 */
int cli_net_add_subnet(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  ip_pfx_t prefix;
  const char * arg;
  uint8_t type;
  net_subnet_t * subnet;
  int result;

  // Subnet prefix
  arg= cli_get_arg_value(cmd, 0);
  if ((str2prefix(arg, &prefix))) {
    cli_set_user_error(cli_get(), "could not add subnet (invalid prefix)");
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Check the prefix length (!= 32)
  // THE CHECK FOR PREFIX LENGTH SHOULD BE MOVED TO THE NETWORK MGMT PART.
  if (prefix.mask == 32) {
    cli_set_user_error(cli_get(), "could not add subnet (invalid subnet)");
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Subnet type: transit / stub ?
  arg= cli_get_arg_value(cmd, 1);
  if (!strcmp(arg, "transit"))
    type= NET_SUBNET_TYPE_TRANSIT;
  else if (!strcmp(arg, "stub"))
    type = NET_SUBNET_TYPE_STUB;
  else {
    cli_set_user_error(cli_get(), "invalid subnet type \"%s\"", arg);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Create new subnet
  subnet= subnet_create(prefix.network, prefix.mask, type);

  // Add the subnet
  result= network_add_subnet(network_get_default(), subnet);
  if (result != ESUCCESS) {
    subnet_destroy(&subnet);
    cli_set_user_error(cli_get(), "could not add subnet (%s)",
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }

  return CLI_SUCCESS;
}
Exemple #9
0
/**
 * context: {link}
 * tokens : {igp-weight}
 * options: {--bidir --tos=<>}
 */
int cli_net_link_igpweight(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  net_iface_t * link= _link_from_context(ctx);
  const char * arg= cli_get_arg_value(cmd, 0);
  const char * opt;
  igp_weight_t weight;
  net_tos_t tos= 0;
  int bidir= 0;
  int result;

  //cli_args_dump(gdsout, cmd->opts);

  // Get optional TOS
  opt= cli_get_opt_value(cmd, "tos");
  if (opt != NULL)
    if (!str2tos(opt, &tos)) {
      cli_set_user_error(cli_get(), "invalid TOS \"%s\"", opt);
      return CLI_ERROR_COMMAND_FAILED;
    }

  // Check option --bidir
  if (cli_has_opt_value(cmd, "bidir")) {
    if (link->type != NET_IFACE_RTR) {
      cli_set_user_error(cli_get(), ": --bidir only works with ptp links");
      return CLI_ERROR_COMMAND_FAILED;
    }
    bidir= 1;
  }

  // Get new IGP weight
  if (str2weight(arg, &weight)) {
    cli_set_user_error(cli_get(), "invalid weight \"%s\"", arg);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Change link weight
  result= net_iface_set_metric(link, tos, weight, bidir);
  if (result != ESUCCESS) {
    cli_set_user_error(cli_get(), "cannot set metric (%s)",
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }

  return CLI_SUCCESS;
}
Exemple #10
0
/**
 * Class:     be_ac_ucl_ingi_cbgp_net_Iface
 * Method:    setWeight
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_be_ac_ucl_ingi_cbgp_net_Interface_setWeight
  (JNIEnv * jEnv, jobject joIface, jlong jlWeight)
{
  net_iface_t * pIface;
  int iResult;

  jni_lock(jEnv);

  pIface= (net_iface_t *) jni_proxy_lookup(jEnv, joIface);
  if (pIface == NULL)
    return_jni_unlock2(jEnv);

  iResult= net_iface_set_metric(pIface, 0, (uint32_t) jlWeight, 0);
  if (iResult != ESUCCESS)
    throw_CBGPException(jEnv, "could not set weight (%s)",
			network_strerror(iResult));

  jni_unlock(jEnv);
}
Exemple #11
0
/*
 * Class:     be_ac_ucl_ingi_cbgp_net_Iface
 * Method:    setCapacity
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_be_ac_ucl_ingi_cbgp_net_Interface_setCapacity
  (JNIEnv * jEnv, jobject joIface, jlong jlCapacity)
{
  net_iface_t * pIface;
  int iResult;

  jni_lock(jEnv);

  /* Get interface */
  pIface= (net_iface_t *) jni_proxy_lookup(jEnv, joIface);
  if (pIface == NULL)
    return_jni_unlock2(jEnv);

  iResult= net_iface_set_capacity(pIface, jlCapacity, 0);
  if (iResult != ESUCCESS)
    throw_CBGPException(jEnv, "could not set capacity (%s)",
			network_strerror(iResult));

  jni_unlock(jEnv);
}
Exemple #12
0
/*
 * Class:     be_ac_ucl_ingi_cbgp_net_Node
 * Method:    addPTMPLink
 * Signature: (Lbe/ac/ucl/ingi/cbgp/net/Subnet;Ljava/lang/String;)Lbe/ac/ucl/ingi/cbgp/net/Link;
 */
JNIEXPORT jobject JNICALL Java_be_ac_ucl_ingi_cbgp_net_Node_addPTMPLink
  (JNIEnv * jEnv, jobject joNode, jobject joDst, jstring jsIface)
{
  net_node_t * pNode;
  net_subnet_t * pSubnet;
  net_iface_t * pIface;
  jobject joIface;
  int iResult;
  net_addr_t tIfaceID;

  jni_lock(jEnv);

  /* Get the node */
  pNode= (net_node_t*) jni_proxy_lookup(jEnv, joNode);
  if (pNode == NULL)
    return_jni_unlock(jEnv, NULL); 

  /* Get the subnet */
  pSubnet= (net_subnet_t *) jni_proxy_lookup(jEnv, joDst);
  if (pSubnet == NULL)
    return_jni_unlock(jEnv, NULL); 

  /* Get the interface ID */
  if (ip_jstring_to_address(jEnv, jsIface, &tIfaceID) != 0)
    return_jni_unlock(jEnv, NULL);  

  /* Add link */
  iResult= net_link_create_ptmp(pNode, pSubnet, tIfaceID, &pIface);
  if (iResult != ESUCCESS) {
    throw_CBGPException(jEnv, "could not create link (%s)",
			network_strerror(iResult));
    return_jni_unlock(jEnv, NULL);
  }

  /* Retrieve new link */
  joIface= cbgp_jni_new_net_Interface(jEnv,
				      NULL/*jni_proxy_get_CBGP(jEnv, joNode)*/,
				      pIface);
  return_jni_unlock(jEnv, joIface);
}
Exemple #13
0
/**
 * context: {iface}
 * tokens: {weight}
 */
static int cli_iface_igpweight(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  net_iface_t * iface= _iface_from_context(ctx);
  const char * arg= cli_get_arg_value(cmd, 0);
  igp_weight_t weight;
  int result;

  // Get IGP weight
  if (str2weight(arg, &weight)) {
    cli_set_user_error(cli_get(), "invalid weight \"%s\"", arg);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Change link weight in forward direction
  result= net_iface_set_metric(iface, 0, weight, 0);
  if (result != ESUCCESS) {
    cli_set_user_error(cli_get(), "cannot set weight (%s)",
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }
  return CLI_SUCCESS;
}
Exemple #14
0
/**
 * context: {}
 * tokens : {file}
 */
int cli_net_traffic_save(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  const char * arg= cli_get_arg_value(cmd, 0);
  gds_stream_t * stream;
  int result;

  // Get the optional parameter
  stream= stream_create_file(arg);
  if (stream == NULL) {
    cli_set_user_error(cli_get(), "could not create \"%d\"", arg);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Save traffic matrix
  result= network_links_save(stream, network_get_default());
  if (result != 0) {
    cli_set_user_error(cli_get(), "could not save traffic matrix (%s)",
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }

  return CLI_SUCCESS;
}
Exemple #15
0
/*
 * Class:     be_ac_ucl_ingi_cbgp_net_Node
 * Method:    addLTLLink
 * Signature: (Lbe/ac/ucl/ingi/cbgp/net/Node;Z)Lbe/ac/ucl/ingi/cbgp/net/Link;
 */
JNIEXPORT jobject JNICALL Java_be_ac_ucl_ingi_cbgp_net_Node_addLTLLink
  (JNIEnv * jEnv, jobject joNode, jobject joDst, jboolean jbBidir)
{
  net_node_t * pNode, *pNodeDst;
  net_iface_t * pIface;
  jobject joLink;
  int iResult;

  jni_lock(jEnv);

  /* Get the node */
  pNode= (net_node_t*) jni_proxy_lookup(jEnv, joNode);
  if (pNode == NULL)
    return_jni_unlock(jEnv, NULL); 

  /* Get the destination */
  pNodeDst= (net_node_t*) jni_proxy_lookup(jEnv, joDst);
  if (pNodeDst == NULL)
    return_jni_unlock(jEnv, NULL); 

  /* Add link */
  iResult= net_link_create_rtr(pNode, pNodeDst,
			       (jbBidir==JNI_TRUE) ? BIDIR : UNIDIR,
			       &pIface);
  if (iResult != ESUCCESS) {
    throw_CBGPException(jEnv, "could not create link (%s)",
			network_strerror(iResult));
    return_jni_unlock(jEnv, NULL);
  }

  /* Retrieve new link */
  joLink= cbgp_jni_new_net_Interface(jEnv,
				NULL/*jni_proxy_get_CBGP(jEnv, joNode)*/,
				pIface);

  return_jni_unlock(jEnv, joLink);
}
Exemple #16
0
/**
 * context: {}
 * tokens: {src-addr, src-iface-id, dst-addr, dst-iface-id}
 */
int cli_net_add_linkptp(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  const char * arg_src   = cli_get_arg_value(cmd, 0);
  const char * arg_src_if= cli_get_arg_value(cmd, 1);
  const char * arg_dst   = cli_get_arg_value(cmd, 2);
  const char * arg_dst_if= cli_get_arg_value(cmd, 3);
  const char * opt;
  net_node_t * src_node;
  net_node_t * dst_node;
  net_iface_id_t src_if;
  net_iface_id_t dst_if;
  int result;
  net_iface_t * iface;
  net_link_delay_t delay= 0;
  net_link_load_t capacity= 0;

  // Get source node
  if (str2node(arg_src, &src_node)) {
    cli_set_user_error(cli_get(), "could not find node \"%s\"", arg_src);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Get source address
  if (net_iface_str2id(arg_src_if, &src_if)) {
    cli_set_user_error(cli_get(), "invalid interface id \"%\"", arg_src_if);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Get destination node
  if (str2node(arg_dst, &dst_node)) {
    cli_set_user_error(cli_get(), "could not find node \"%s\"", arg_dst);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // get destination address
  if (net_iface_str2id(arg_dst_if, &dst_if)) {
    cli_set_user_error(cli_get(), "invalid interface id \"%\"", arg_dst_if);
    return CLI_ERROR_COMMAND_FAILED;
  }

  // Get optional link capacity
  opt= cli_get_opt_value(cmd, "bw");
  if (opt != NULL) {
    if (str2capacity(opt, &capacity)) {
      cli_set_user_error(cli_get(), "invalid link capacity \"%s\"", opt);
      return CLI_ERROR_COMMAND_FAILED;
    }
  }

  // Get optional link delay
  opt= cli_get_opt_value(cmd, "delay");
  if (opt != NULL) {
    if (str2delay(opt, &delay)) {
      cli_set_user_error(cli_get(), "invalid link delay \"%s\"", opt);
      return CLI_ERROR_CMD_FAILED;
    }
  }
  
  // Create link
  result= net_link_create_ptp(src_node, src_if, dst_node, dst_if,
			      BIDIR, &iface);
  if (result != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not create ptp link "
		       "from %s (%s) to %s (%s) (%s)",
		       arg_src, arg_src_if, arg_dst, arg_dst_if,
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }

  result= net_link_set_phys_attr(iface, delay, capacity, BIDIR);
  if (result != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not set physical attributes (%s)",
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }

  return CLI_SUCCESS;
}
Exemple #17
0
/**
 * context: {}
 * tokens: {addr-src, prefix-dst}
 * options: [--depth=, --capacity=, delay=]
 */
int cli_net_add_link(cli_ctx_t * ctx, cli_cmd_t * cmd)
{
  const char * arg_src= cli_get_arg_value(cmd, 0);
  const char * arg_dst= cli_get_arg_value(cmd, 1);
  const char * opt;
  net_node_t * src_node;
  net_node_t * dst_node;
  ip_dest_t dest;
  int result;
  uint8_t depth= 1;
  net_link_delay_t delay= 0;
  net_link_load_t capacity= 0;
  net_iface_dir_t dir;
  net_iface_t * iface;
  net_subnet_t * subnet;

  // Get optional link depth
  opt= cli_get_opt_value(cmd, "depth");
  if (opt != NULL) {
    if (str2depth(opt, &depth)) {
      cli_set_user_error(cli_get(), "invalid link depth \"%s\"", opt);
      return CLI_ERROR_COMMAND_FAILED;
    }
  }
  
  // Get optional link capacity
  opt= cli_get_opt_value(cmd, "bw");
  if (opt != NULL) {
    if (str2capacity(opt, &capacity)) {
      cli_set_user_error(cli_get(), "invalid link capacity \"%s\"", opt);
      return CLI_ERROR_COMMAND_FAILED;
    }
  }

  // Get optional link delay
  opt= cli_get_opt_value(cmd, "delay");
  if (opt != NULL) {
    if (str2delay(opt, &delay)) {
      cli_set_user_error(cli_get(), "invalid link delay \"%s\"", opt);
      return CLI_ERROR_CMD_FAILED;
    }
  }

  // Get source node
  if (str2node(arg_src, &src_node)) {
    cli_set_user_error(cli_get(), "could not find node \"%s\"", arg_src);
    return CLI_ERROR_COMMAND_FAILED;
  }
  
  // Get destination: can be a node / a subnet
  if ((ip_string_to_dest(arg_dst, &dest) < 0) ||
      (dest.type == NET_DEST_ANY)) {
    cli_set_user_error(cli_get(), "invalid destination \"%s\".", arg_dst);
    return CLI_ERROR_COMMAND_FAILED;
  }
  
  // Add link: RTR (to node) / PTMP (to subnet)
  if (dest.type == NET_DEST_ADDRESS) {
    dst_node= network_find_node(network_get_default(), dest.addr);
    if (dst_node == NULL) {
      cli_set_user_error(cli_get(), "tail-end \"%s\" does not exist.",
			 arg_dst);
      return CLI_ERROR_COMMAND_FAILED;
    }
    dir= BIDIR;
    result= net_link_create_rtr(src_node, dst_node, dir, &iface);
  } else {
    subnet= network_find_subnet(network_get_default(), dest.prefix);
    if (subnet == NULL) {
      cli_set_user_error(cli_get(), "tail-end \"%s\" does not exist.",
			 arg_dst);
      return CLI_ERROR_COMMAND_FAILED;
    }
    dir= UNIDIR;
    result= net_link_create_ptmp(src_node, subnet,
				 dest.prefix.network,
				 &iface);
  }
  if (result != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not add link %s -> %s (%s)",
		       arg_src, arg_dst, network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }

  result= net_link_set_phys_attr(iface, delay, capacity, dir);
  if (result != ESUCCESS) {
    cli_set_user_error(cli_get(), "could not set physical attributes (%s)",
		       network_strerror(result));
    return CLI_ERROR_COMMAND_FAILED;
  }  

  return CLI_SUCCESS;
}