/* * $Id: srsvp.c,v 2.2 2000/05/23 08:04:56 masaki Exp $ */ #include "ricd.h" #include "config_file.h" static void srsvp_neighbor_down (srsvp_t *srsvp, srsvp_neighbor_t *neighbor); static void srsvp_neighbor_start (srsvp_t *srsvp, srsvp_neighbor_t *neighbor); static int srsvp_process_pdu (srsvp_t *srsvp, srsvp_neighbor_t *neighbor); static srsvp_neighbor_t *srsvp_create_neighbor2 (srsvp_t *srsvp, prefix_t *prefix, interface_t *interface, int routerf); static int srsvp_resolv_upstream (srsvp_t *srsvp, srsvp_flow_t *flow); static int srsvp_i_am_sender (srsvp_t *srsvp, srsvp_flow_t *flow); static void srsvp_destroy_flow (srsvp_t *srsvp, srsvp_flow_t *flow); static void srsvp_destroy_leaf (srsvp_t *srsvp, srsvp_leaf_t *leaf); static char *srsvp_error_spec_string (u_long errcode, char *text); static srsvp_flow_t *srsvp_flow_find (srsvp_t *srsvp, srsvp_flow_t *flow); static srsvp_flow_t *srsvp_flow_copy_add (srsvp_t *srsvp, srsvp_flow_t *flow); static srsvp_neighbor_t *srsvp_get_upstream (srsvp_t *srsvp, prefix_t *sender, req_qos_t *req_qos, u_long *errcode_p); static char *srsvp_pdus[] = { "", "PATH", "RESV", "PATH_ERR", "RESV_ERR", "PATH_TEAR", "RESV_TEAR", }; req_qos_t * copy_req_qos (req_qos_t *src, req_qos_t *dst) { assert (src); if (src == NULL) return (NULL); if (dst == NULL) { dst = New (req_qos_t); } *dst = *src; return (dst); } static void srsvp_notify_hqlip (srsvp_t *srsvp, srsvp_flow_t *flow, srsvp_leaf_t *leaf, int on) { hqlip_t *hqlip = RICD->hqlip; srsvp_interface_t *vif; req_qos_t *req_qos; #ifdef HAVE_IPV6 if (srsvp->family == AF_INET6) hqlip = RICD6->hqlip; #endif /* HAVE_IPV6 */ assert (leaf->neighbor); vif = leaf->neighbor->vif; req_qos = copy_req_qos (leaf->req_qos, NULL); schedule_event2 ("hqlip_link_status", hqlip->schedule, (event_fn_t) hqlip_link_status, 4, hqlip, req_qos, vif->interface, on); } static void trace_flow (u_long flags, trace_t *tr, srsvp_flow_t *flow, char *header) { if (flow == NULL) return; trace (flags, tr, "%s flow %a proto %d port %d sender %a sport %d\n", header, flow->destin, flow->proto, flow->dport, flow->sender, flow->sport); } static void trace_neighbor (u_long flags, trace_t *tr, srsvp_neighbor_t *neighbor, char *header) { if (neighbor == NULL) return; trace (flags, tr, "%s %a lih %d on %s\n", header, neighbor->prefix, neighbor->lih, neighbor->vif->interface->name); } static void trace_req_qos (u_long flags, trace_t *tr, req_qos_t *req_qos, char *header) { if (req_qos == NULL) return; trace (flags, tr, " %s pri %u mtu %u pps %u " "sec %u cd %u cf %u rdly %u rfee %u\n", header, req_qos->pri, req_qos->mtu, req_qos->pps, req_qos->sec, req_qos->cd, req_qos->cf, req_qos->rdly, req_qos->rfee); } static int srsvp_write (srsvp_neighbor_t *neighbor, u_char *data, int len) { int ret; if (neighbor->sockfd < 0) return (-1); ret = write (neighbor->sockfd, data, len); if (ret < 0) { trace (TR_ERROR, neighbor->trace, "write failed %m on fd %d\n", neighbor->sockfd); } else { trace (TR_PACKET, neighbor->trace, "write %d bytes\n", ret); neighbor->num_packets_sent++; } return (ret); } #ifndef HAVE_LIBPTHREAD static void destroy_packet (packet_t *packet) { Delete (packet->data); Delete (packet); } static int srsvp_flush_queue (srsvp_t *srsvp, srsvp_neighbor_t * neighbor) { packet_t *packet; int ret; for (;;) { pthread_mutex_lock (&neighbor->send_mutex_lock); packet = LL_GetHead (neighbor->send_queue); if (packet == NULL) { /* end of queue */ pthread_mutex_unlock (&neighbor->send_mutex_lock); return (1); } LL_RemoveFn (neighbor->send_queue, packet, NULL); pthread_mutex_unlock (&neighbor->send_mutex_lock); ret = srsvp_write (neighbor, packet->data, packet->len); if (ret == 0) { /* try again */ pthread_mutex_lock (&neighbor->send_mutex_lock); LL_Prepend (neighbor->send_queue, packet); select_enable_fd_mask (neighbor->sockfd, SELECT_WRITE); pthread_mutex_unlock (&neighbor->send_mutex_lock); return (0); } destroy_packet (packet); if (ret < 0) return (ret); } return (0); } #endif /* HAVE_LIBPTHREAD */ static int srsvp_send_message (srsvp_t *srsvp, srsvp_neighbor_t *neighbor, int type, u_long srbit, int len, u_char *data) { int flags = 0; int ttl = 255; /* XXX ? */ assert (type >= SRSVP_MSG_PATH && type <= SRSVP_MSG_RESV_TEAR); assert (len >= 0 && len + SRSVP_MSG_HDR_SIZE < SRSVP_MSG_SIZE); if (neighbor->sockfd < 0) return (-1); { #ifdef HAVE_LIBPTHREAD u_char msgbuf[SRSVP_MSG_SIZE]; u_char *cp = msgbuf; #else u_char *cp = NewArray (u_char, len + SRSVP_MSG_HDR_SIZE); packet_t *packet = New (packet_t); packet->data = cp; packet->len = len + SRSVP_MSG_HDR_SIZE; #endif /* HAVE_LIBPTHREAD */ MRT_PUT_BYTE ((SRSVP_MSG_VERSION << 4) | (flags & 0x0f), cp); MRT_PUT_BYTE (type, cp); MRT_PUT_SHORT (0, cp); MRT_PUT_BYTE (ttl, cp); MRT_PUT_BYTE (srbit, cp); MRT_PUT_SHORT (len + SRSVP_MSG_HDR_SIZE, cp); /* includes common header */ if (len > 0) memcpy (cp, data, len); #ifdef HAVE_LIBPTHREAD /* send it directly since I am a thread */ return (srsvp_write (neighbor, msgbuf, len + SRSVP_MSG_HDR_SIZE)); #else pthread_mutex_lock (&neighbor->send_mutex_lock); LL_Append (neighbor->send_queue, packet); if (LL_GetCount (neighbor->send_queue) == 1) { select_enable_fd_mask (neighbor->sockfd, SELECT_WRITE); } pthread_mutex_unlock (&neighbor->send_mutex_lock); trace (TR_PACKET, neighbor->trace, "send %s (%d bytes) queued\n", srsvp_pdus[type], len + SRSVP_MSG_HDR_SIZE); return (0); #endif /* HAVE_LIBPTHREAD */ } } static int srsvp_read (srsvp_neighbor_t *neighbor, u_char *ptr, int len) { int n; if (neighbor->sockfd < 0) return (-1); n = read (neighbor->sockfd, ptr, len); if (n < 0) { switch (errno) { case EWOULDBLOCK: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK case EAGAIN: #endif /* EAGAIN */ case EINTR: case ENETUNREACH: case EHOSTUNREACH: trace (TR_INFO, neighbor->trace, "READ FAILED on %d (%m) -- OKAY TO IGNORE\n", neighbor->sockfd); return (0); default: trace (TR_WARN, neighbor->trace, "READ FAILED on %d (%m)\n", neighbor->sockfd); return (-1); } } else if (n == 0) { trace (TR_WARN, neighbor->trace, "READ FAILED on %d EOF???\n", neighbor->sockfd); return (-1); } trace (TR_PACKET, neighbor->trace, "read %d bytes\n", n); return (n); } static int srsvp_fill_packet (srsvp_neighbor_t *neighbor) { int len, n; assert (neighbor); assert (neighbor->read_ptr >= neighbor->buffer); assert (neighbor->read_ptr <= neighbor->buffer + sizeof (neighbor->buffer)); assert (neighbor->start_ptr >= neighbor->buffer); assert (neighbor->start_ptr <= neighbor->buffer + sizeof (neighbor->buffer)); if ((len = neighbor->read_ptr - neighbor->start_ptr) == 0) { /* reset the pointers */ neighbor->start_ptr = neighbor->buffer; neighbor->read_ptr = neighbor->buffer; } if (neighbor->buffer + sizeof (neighbor->buffer) - neighbor->read_ptr < SRSVP_MSG_SIZE) { /* need to move them to the start to get more */ memcpy (neighbor->buffer, neighbor->start_ptr, len); neighbor->start_ptr = neighbor->buffer; neighbor->read_ptr = neighbor->buffer + len; } if ((n = srsvp_read (neighbor, neighbor->read_ptr, SRSVP_MSG_SIZE)) < 0) { return (-1); } else if (n == 0) { return (0); } neighbor->read_ptr += n; assert (neighbor->read_ptr <= neighbor->buffer + sizeof (neighbor->buffer)); return (len + n); } static int srsvp_get_packet (srsvp_neighbor_t *neighbor) { int pdu_len, len; u_char *cp; neighbor->packet = NULL; /* need to be filled at least a header in buffer */ /* check if the requested length of data already in buffer */ if ((len = neighbor->read_ptr - neighbor->start_ptr) < SRSVP_MSG_HDR_SIZE) { return (0); } cp = neighbor->start_ptr; SRSVP_PEEK_HDRLEN (pdu_len, cp); if (pdu_len < SRSVP_MSG_HDR_SIZE || pdu_len > SRSVP_MSG_SIZE) { neighbor->start_ptr = neighbor->read_ptr; /* eat up the input */ trace (TR_WARN, neighbor->trace, "wrong message size %d\n", pdu_len); return (-1); } /* see if the total length packet in buffer */ /* check if the requested length of data already in buffer */ if (len < pdu_len) { return (0); } neighbor->packet = neighbor->start_ptr; neighbor->start_ptr += pdu_len; return (1); } static int srsvp_get_pdu (srsvp_t *srsvp, srsvp_neighbor_t * neighbor) { int ret; /* I know that the return value will not be used, but leave as it was */ if ((ret = srsvp_fill_packet (neighbor)) < 0) { srsvp_neighbor_down (srsvp, neighbor); return (-1); } else if (ret == 0) { assert (neighbor->sockfd >= 0); select_enable_fd_mask (neighbor->sockfd, SELECT_READ); return (ret); } for (;;) { if ((ret = srsvp_get_packet (neighbor)) < 0) { srsvp_neighbor_down (srsvp, neighbor); return (-1); } else if (ret == 0) { break; } if ((ret = srsvp_process_pdu (srsvp, neighbor)) < 0) { srsvp_neighbor_down (srsvp, neighbor); return (-1); } } if (neighbor->sockfd >= 0) select_enable_fd_mask (neighbor->sockfd, SELECT_READ); return (1); } static void srsvp_connect_ready (srsvp_t *srsvp, srsvp_neighbor_t *neighbor) { sockunion_t name; int namelen = sizeof (name); if (BIT_TEST (neighbor->flags, SRSVP_OPEN_IN_PROGRESS)) { Timer_Turn_OFF (neighbor->open_timeout); BIT_RESET (neighbor->flags, SRSVP_OPEN_IN_PROGRESS); } if (neighbor->sockfd < 0) { trace (TR_WARN, neighbor->trace, "connect to %a succeeded but sockfd %d has been closed\n", neighbor->prefix, neighbor->sockfd); return; } /* see if we are really connected */ if (getpeername (neighbor->sockfd, (struct sockaddr *) &name, &namelen) < 0) { trace (TR_INFO, neighbor->trace, "connect to %a failed (%m)\n", neighbor->prefix); srsvp_neighbor_down (srsvp, neighbor); return; } trace (TR_INFO, neighbor->trace, "outgoing connection succeeded on %d\n", neighbor->sockfd); #ifndef HAVE_LIBPTHREAD socket_set_nonblocking (neighbor->sockfd, 0); #endif /* HAVE_LIBPTHREAD */ timer_set_flags (neighbor->open_retry, TIMER_EXPONENT_SET, 0); srsvp_neighbor_start (srsvp, neighbor); } static int srsvp_tcp_connect (srsvp_t *srsvp, srsvp_neighbor_t *neighbor) { int ret, port = SRSVP_TCP_PORT; int family, len; sockunion_t anyaddr; prefix_t *local = neighbor->vif->prefix; memset (&anyaddr, 0, sizeof (anyaddr)); /* initiate a TCP connection */ family = neighbor->prefix->family; #ifdef HAVE_IPV6 if (family == AF_INET6) { anyaddr.sin6.sin6_family = family; anyaddr.sin6.sin6_port = htons (port); memcpy (&anyaddr.sin6.sin6_addr, prefix_tochar (neighbor->prefix), 16); len = sizeof (anyaddr.sin6); } else #endif /* HAVE_IPV6 */ { anyaddr.sin.sin_family = family; anyaddr.sin.sin_port = htons (port); memcpy (&anyaddr.sin.sin_addr, prefix_tochar (neighbor->prefix), 4); len = sizeof (anyaddr.sin); } if ((neighbor->sockfd = socket (family, SOCK_STREAM, 0)) < 0) { trace (TR_ERROR, srsvp->trace, "socket open failed (%m)\n"); return (-1); } if (local) { /* port will not be bound */ if (socket_bind_port (neighbor->sockfd, family, prefix_tochar (local), 0) < 0 ) { return (-1); } } #ifndef HAVE_LIBPTHREAD /* always non-blocking. if connect doesn't return, there is no way to resume it. */ socket_set_nonblocking (neighbor->sockfd, 1); #endif /* HAVE_LIBPTHREAD */ if (BIT_TEST (neighbor->flags, SRSVP_OPEN_RETRY)) { BIT_RESET (neighbor->flags, SRSVP_OPEN_RETRY); } BIT_SET (neighbor->flags, SRSVP_OPEN_IN_PROGRESS); trace (TR_TRACE, neighbor->trace, "initiating connect to %a on sockfd %d\n", neighbor->prefix, neighbor->sockfd); ret = connect (neighbor->sockfd, (struct sockaddr *)& anyaddr, len); if (ret < 0) { if (errno != EINPROGRESS) { /* wait open timeout to delete the neighbor */ return (-1); } trace (TR_PACKET, srsvp->trace, "waiting on %d for write\n", neighbor->sockfd); BIT_SET (neighbor->flags, SRSVP_OPEN_IN_PROGRESS); Timer_Turn_ON (neighbor->open_timeout); select_add_fd_event ("srsvp_connect_ready", neighbor->sockfd, SELECT_WRITE, TRUE, neighbor->schedule, srsvp_connect_ready, 2, srsvp, neighbor); return (0); } srsvp_connect_ready (srsvp, neighbor); return (1); } static void srsvp_open_retry (srsvp_t *srsvp, srsvp_neighbor_t *neighbor) { if (srsvp_tcp_connect (srsvp, neighbor) < 0) Timer_Turn_ON (neighbor->open_retry); } static void srsvp_neighbor_down (srsvp_t *srsvp, srsvp_neighbor_t *neighbor) { if (BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_DELETED)) return; if (BIT_TEST (neighbor->flags, SRSVP_OPEN_IN_PROGRESS)) { Timer_Turn_OFF (neighbor->open_timeout); BIT_RESET (neighbor->flags, SRSVP_OPEN_IN_PROGRESS); } if (BIT_TEST (neighbor->flags, SRSVP_OPEN_RETRYING)) { Timer_Turn_OFF (neighbor->open_retry); BIT_RESET (neighbor->flags, SRSVP_OPEN_RETRYING); } if (BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_CONNECTED)) { Timer_Turn_OFF (neighbor->keep_alive); BIT_RESET (neighbor->flags, SRSVP_NEIGHBOR_CONNECTED); } trace (TR_WARN, neighbor->trace, "neighbor %a on %s going down\n", neighbor->prefix, neighbor->vif->interface->name); time (&neighbor->utime); if (neighbor->sockfd >= 0) { select_delete_fd (neighbor->sockfd); /* if (select_delete_fd (neighbor->sockfd) < 0) close (neighbor->sockfd); */ neighbor->sockfd = -1; } #ifndef HAVE_LIBPTHREAD LL_Clear (neighbor->send_queue); #endif /* HAVE_LIBPTHREAD */ neighbor->read_ptr = neighbor->buffer; neighbor->start_ptr = neighbor->buffer; if (BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_ROUTER)) { if (prefix_compare_wolen (neighbor->prefix, neighbor->vif->prefix) > 0) { BIT_SET (neighbor->flags, SRSVP_OPEN_RETRYING); Timer_Turn_ON (neighbor->open_retry); } } } static void srsvp_keep_alive (srsvp_t *srsvp, srsvp_neighbor_t *neighbor) { if (!BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_CONNECTED)) return; if (neighbor->sockfd < 0) return; if (get_socket_addr (neighbor->sockfd, 0, NULL) < 0) { trace (TR_WARN, neighbor->trace, "not connected (%m)\n"); srsvp_neighbor_down (srsvp, neighbor); return; } } static void srsvp_open_accept (srsvp_t *srsvp, srsvp_interface_t *vif) { int new_sockfd, len; sockunion_t remote; prefix_t *local_prefix = NULL, *remote_prefix; srsvp_neighbor_t *neighbor; interface_t *interface = vif->interface; int sockfd = vif->tcp_sockfd; if (sockfd < 0) return; len = sizeof (remote); if ((new_sockfd = accept (sockfd, (struct sockaddr *) &remote, &len)) < 0) { trace (TR_ERROR, srsvp->trace, "accept (%m)\n"); select_enable_fd_mask (sockfd, SELECT_READ); return; } select_enable_fd_mask (sockfd, SELECT_READ); remote_prefix = sockaddr_toprefix ((struct sockaddr *) &remote); if (remote_prefix->family != srsvp->family) { trace (TR_WARN, srsvp->trace, "recv open from %a on %s but family must be %s\n", remote_prefix, vif->interface->name, family2string (srsvp->family)); Deref_Prefix (remote_prefix); close (new_sockfd); return; } if (!BITX_TEST (&srsvp->interface_mask, interface->index)) { /* must not happen */ trace (TR_ERROR, srsvp->trace, "connection from %a on %s refused (interface disabled)\n", remote_prefix, interface->name); Deref_Prefix (remote_prefix); close (new_sockfd); return; } if (get_socket_addr (sockfd, 0, &local_prefix) < 0) { trace (TR_ERROR, srsvp->trace, "getsockname (%m)\n"); Deref_Prefix (remote_prefix); close (new_sockfd); return; } assert (local_prefix); if (local_prefix == NULL || prefix_is_unspecified (local_prefix)) { Deref_Prefix (local_prefix); local_prefix = Ref_Prefix (vif->prefix); } else { if (!address_equal (vif->prefix, local_prefix)) { trace (TR_ERROR, srsvp->trace, "connection from %a to %a on %s refused " "(local address unknown)\n", remote_prefix, local_prefix, interface->name); Deref_Prefix (local_prefix); Deref_Prefix (remote_prefix); close (new_sockfd); return; } } if (remote_prefix->family != local_prefix->family) { trace (TR_ERROR, srsvp->trace, "connection from %a to %a on %s refused (family mismatch)\n", remote_prefix, local_prefix, interface->name); Deref_Prefix (local_prefix); Deref_Prefix (remote_prefix); close (new_sockfd); return; } if (address_equal (remote_prefix, local_prefix)) { trace (TR_ERROR, srsvp->trace, "connection from %a to %a on %s refused (myself)\n", remote_prefix, local_prefix, interface->name); Deref_Prefix (local_prefix); Deref_Prefix (remote_prefix); close (new_sockfd); return; } LL_Iterate (vif->ll_neighbors, neighbor) { if (address_equal (neighbor->prefix, remote_prefix)) break; } if (neighbor == NULL) { trace (TR_INFO, srsvp->trace, "connection from %a to %a on %s accepted (host)\n", remote_prefix, local_prefix, interface->name); neighbor = srsvp_create_neighbor2 (srsvp, Ref_Prefix (remote_prefix), interface, 0); } else { if (neighbor->sockfd >= 0) { trace (TR_ERROR, srsvp->trace, "connection from %a to %a on %s refused " "(already connected)\n", remote_prefix, local_prefix, interface->name); Deref_Prefix (local_prefix); Deref_Prefix (remote_prefix); close (new_sockfd); #ifdef notdef /* probably, the existing is dead (but this is weak to atack) */ srsvp_neighbor_down (srsvp, neighbor); #endif return; } if (BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_ROUTER)) { /* tcp connection must initiate from small to big */ if (prefix_compare_wolen (remote_prefix, local_prefix) >= 0) { trace (TR_ERROR, srsvp->trace, "connection from %a to %a on %s refused " "(reverse direction)\n", remote_prefix, local_prefix, interface->name); Deref_Prefix (local_prefix); Deref_Prefix (remote_prefix); close (new_sockfd); return; } } trace (TR_INFO, srsvp->trace, "connection from %a to %a on %s accepted (%s)\n", remote_prefix, local_prefix, interface->name, BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_ROUTER)? "router": "host"); } neighbor->sockfd = new_sockfd; Deref_Prefix (local_prefix); Deref_Prefix (remote_prefix); srsvp_neighbor_start (srsvp, neighbor); } static srsvp_neighbor_t * srsvp_create_neighbor2 (srsvp_t *srsvp, prefix_t *prefix, interface_t *interface, int routerf) { char name[MAXLINE]; srsvp_interface_t *vif; srsvp_neighbor_t *neighbor; if (srsvp == NULL) { ricd_t *ricd = RICD; #ifdef HAVE_IPV6 if (prefix->family == AF_INET6) ricd = RICD6; #endif /* HAVE_IPV6 */ schedule_event2 ("srsvp_create_neighbor2", ricd->srsvp->schedule, (event_fn_t) srsvp_create_neighbor2, 4, ricd->srsvp, prefix, interface, routerf); return (NULL); } vif = srsvp->srsvp_interfaces[interface->index]; if (vif == NULL) { Deref_Prefix (prefix); return (NULL); } LL_Iterate (vif->ll_neighbors, neighbor) { if (address_equal (neighbor->prefix, prefix)) break; } if (neighbor == NULL) { neighbor = New (srsvp_neighbor_t); neighbor->prefix = Ref_Prefix (prefix); neighbor->trace = trace_copy (srsvp->trace); sprintf (name, "SRSVP %s", prefix_toa (prefix)); set_trace (neighbor->trace, TRACE_PREPEND_STRING, name, 0); neighbor->sockfd = -1; neighbor->vif = vif; #ifndef HAVE_LIBPTHREAD neighbor->send_queue = LL_Create (LL_DestroyFunction, destroy_packet, 0); #endif /* HAVE_LIBPTHREAD */ neighbor->read_ptr = neighbor->buffer; neighbor->start_ptr = neighbor->buffer; time (&neighbor->ctime); time (&neighbor->utime); neighbor->schedule = New_Schedule (name, neighbor->trace); mrt_thread_create2 (name, neighbor->schedule, NULL, NULL); LL_Add2 (vif->ll_neighbors, neighbor); if (vif->prefix) { if (address_equal (prefix, vif->prefix)) { /* loop back */ vif->myself = neighbor; } } else if (is_prefix_local_on (prefix, interface)) { /* loop back */ vif->myself = neighbor; } trace (TR_PACKET, neighbor->trace, "created on %s as %s\n", interface->name, routerf? "router": "host"); } else if (routerf && !BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_ROUTER)) { trace (TR_PACKET, neighbor->trace, "updated on %s as %s\n", interface->name, routerf? "router": "host"); } BIT_RESET (neighbor->flags, SRSVP_NEIGHBOR_DELETED); if (routerf && !BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_ROUTER)) { BIT_SET (neighbor->flags, SRSVP_NEIGHBOR_ROUTER); if (neighbor->open_retry == NULL) { neighbor->open_retry = New_Timer2 ("srsvp_connect_retry", SRSVP_OPEN_RETRY, TIMER_ONE_SHOT, neighbor->schedule, (void_fn_t)srsvp_open_retry, 2, srsvp, neighbor); timer_set_flags (neighbor->open_retry, TIMER_EXPONENT); timer_set_flags (neighbor->open_retry, TIMER_EXPONENT_MAX, SRSVP_OPEN_RETRY_MAX); } if (neighbor->open_timeout == NULL) neighbor->open_timeout = New_Timer2 ("srsvp_connect_timeout", SRSVP_OPEN_TIMEOUT, TIMER_ONE_SHOT, neighbor->schedule, srsvp_neighbor_down, 2, srsvp, neighbor); } if (neighbor->keep_alive == NULL) /* need to check a tcp connection peridodically */ /* this is not needed since select() wakes up on read when the connection was lost */ neighbor->keep_alive = New_Timer2 ("srsvp_keep_alive", SRSVP_KEEP_ALIVE, 0, neighbor->schedule, srsvp_keep_alive, 2, srsvp, neighbor); if (neighbor->sockfd < 0 && BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_ROUTER)) { /* I need to know my address to compare */ if (vif->prefix == NULL) { Deref_Prefix (prefix); return (neighbor); } /* tcp connection must initiate from small to big */ if (prefix_compare_wolen (prefix, vif->prefix) > 0) { srsvp_tcp_connect (srsvp, neighbor); } } Deref_Prefix (prefix); return (neighbor); } void srsvp_create_neighbor (srsvp_t *srsvp, prefix_t *prefix, interface_t *interface) { srsvp_create_neighbor2 (srsvp, Ref_Prefix (prefix), interface, 1); } void srsvp_delete_neighbor (srsvp_t *srsvp, prefix_t *prefix, interface_t *interface) { srsvp_interface_t *vif; srsvp_neighbor_t *neighbor; if (srsvp == NULL) { ricd_t *ricd = RICD; if (prefix->family == AF_INET6) ricd = RICD6; schedule_event2 ("srsvp_delete_neighbor", ricd->srsvp->schedule, (event_fn_t) srsvp_delete_neighbor, 3, ricd->srsvp, prefix, interface); return; } vif = srsvp->srsvp_interfaces[interface->index]; if (vif == NULL) return; LL_Iterate (vif->ll_neighbors, neighbor) { if (address_equal (neighbor->prefix, prefix)) break; } if (neighbor == NULL) return; if (BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_DELETED)) return; if (neighbor->sockfd >= 0) { select_delete_fd (neighbor->sockfd); neighbor->sockfd = -1; } BIT_SET (neighbor->flags, SRSVP_NEIGHBOR_DELETED); srsvp_neighbor_down (srsvp, neighbor); } void srsvp_start (srsvp_t *srsvp) { } void srsvp_init (ricd_t *ricd) { char *name = "SRSVP"; #ifdef HAVE_IPV6 if (ricd->family == AF_INET6) { name = "SRSVP6"; } #endif /* HAVE_IPV6 */ ricd->srsvp = New (srsvp_t); ricd->srsvp->trace = trace_copy (ricd->trace); set_trace (ricd->srsvp->trace, TRACE_PREPEND_STRING, name, 0); ricd->srsvp->family = ricd->family; ricd->srsvp->ll_srsvp_interfaces = LL_Create (0); memset (&ricd->srsvp->interface_mask, 0, sizeof (ricd->srsvp->interface_mask)); ricd->srsvp->schedule = New_Schedule (name, ricd->trace); mrt_thread_create2 (name, ricd->srsvp->schedule, NULL, NULL); qif_close (); qif_init (); /* XXX where i can close it? */ } static int srsvp_vif_tcp_init (srsvp_t *srsvp, srsvp_interface_t *vif) { int sockfd; u_char *bind_addr = (vif->prefix)? prefix_tochar (vif->prefix): NULL; if (vif->tcp_sockfd >= 0) { select_delete_fd (vif->tcp_sockfd); vif->tcp_sockfd = -1; } sockfd = socket_open (srsvp->family, SOCK_STREAM, 0); if (sockfd < 0) return (sockfd); socket_reuse (sockfd, 1); socket_bind_port (sockfd, srsvp->family, bind_addr, SRSVP_TCP_PORT); listen (sockfd, 5); vif->tcp_sockfd = sockfd; select_add_fd_event ("srsvp_open_accept", sockfd, SELECT_READ, TRUE, srsvp->schedule, srsvp_open_accept, 2, srsvp, vif); return (sockfd); } static void srsvp_set_vif_prefix (srsvp_t *srsvp, srsvp_interface_t *vif, prefix_t *prefix) { Deref_Prefix (vif->prefix); if (prefix == NULL) { #ifdef HAVE_IPV6 if (srsvp->family == AF_INET6) vif->prefix = vif->interface->primary6->prefix; else #endif /* HAVE_IPV6 */ vif->prefix = vif->interface->primary->prefix; } else { vif->prefix = Ref_Prefix (prefix); } } void srsvp_activate_interface (srsvp_t *srsvp, interface_t *interface, prefix_t *local, int on) { srsvp_interface_t *vif; vif = srsvp->srsvp_interfaces [interface->index]; if (on > 0 && BITX_TEST (&srsvp->interface_mask, interface->index)) { /* updating network */ assert (vif); if (local && !address_equal (vif->prefix, local)) { srsvp_set_vif_prefix (srsvp, vif, local); srsvp_vif_tcp_init (srsvp, vif); } } else if (on > 0 && !BITX_TEST (&srsvp->interface_mask, interface->index)) { /* new */ if (vif != NULL) { Deref_Prefix (vif->prefix); srsvp_set_vif_prefix (srsvp, vif, local); } else { vif = New (srsvp_interface_t); vif->interface = interface; vif->ll_neighbors = LL_Create (0); vif->tcp_sockfd = -1; vif->qalg = 0; /* XXX */ vif->qlimit = -1; /* XXX */ srsvp->srsvp_interfaces [interface->index] = vif; srsvp_set_vif_prefix (srsvp, vif, local); LL_Add2 (srsvp->ll_srsvp_interfaces, vif); /* add qif needs vif->prefix set */ qif_add_qif (srsvp, vif); } BITX_SET (&srsvp->interface_mask, interface->index); assert (vif->tcp_sockfd < 0); srsvp_vif_tcp_init (srsvp, vif); } else if (on < 0 && BITX_TEST (&srsvp->interface_mask, interface->index)) { qif_del_qif (srsvp, vif); BITX_RESET (&srsvp->interface_mask, interface->index); vif = srsvp->srsvp_interfaces [interface->index]; assert (vif); if (vif->tcp_sockfd >= 0) { /* interface_mask was off, hello timer off so that gracefully stopping by protocol timeout */ select_delete_fd (vif->tcp_sockfd); vif->tcp_sockfd = -1; } Deref_Prefix (vif->prefix); vif->prefix = NULL; } } /* XXX called from another thread */ int srsvp_show_neighbors (uii_connection_t *uii, char *ip, char *ifname) { srsvp_interface_t *vif; srsvp_neighbor_t *neighbor; time_t now; interface_t *interface = NULL; ricd_t *ricd = RICD; srsvp_t *srsvp; if (strcasecmp (ip, "ipv6") == 0) ricd = RICD6; Delete (ip); if (ricd == NULL || ricd->srsvp == NULL) return (0); srsvp = ricd->srsvp; if (ifname) { interface = find_interface_byname (ifname); Delete (ifname); if (interface == NULL) { return (-1); } } time (&now); uii_add_bulk_output (uii, "%-35s %7s %8s %3s %3s %11s\n", "Neighbor Address", "If", "Time", "UP", "R/H", "Sent/Rcvd"); LL_Iterate (srsvp->ll_srsvp_interfaces, vif) { if (interface != NULL && interface != vif->interface) continue; if (vif->ll_neighbors == NULL || LL_GetCount (vif->ll_neighbors) == 0) continue; LL_Iterate (vif->ll_neighbors, neighbor) { char buff[64], strbuf[64] = ""; if (vif->myself == neighbor) sprintf (strbuf, " (self)"); else if (BIT_TEST (neighbor->flags, SRSVP_OPEN_IN_PROGRESS)) sprintf (strbuf, " (connecting)"); else if (BIT_TEST (neighbor->flags, SRSVP_OPEN_RETRYING)) sprintf (strbuf, " (retry wait)"); else if (BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_CONNECTED)) sprintf (strbuf, " (connected)"); if (!BITX_TEST (&srsvp->interface_mask, vif->interface->index)) sprintf (strbuf, " (deleted)"); uii_add_bulk_output (uii, "%-35a %7s %8s %3d %3s %5d/%-5d %s\n", neighbor->prefix, neighbor->vif->interface->name, time2date (now - neighbor->utime, buff), neighbor->num_session_up, BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_ROUTER)? " R ": " H ", neighbor->num_packets_sent, neighbor->num_packets_recv, strbuf); } } return (1); } static int srsvp_forward_msg (srsvp_t *srsvp, int type, int srbit, srsvp_flow_t *flow, srsvp_neighbor_t *neighbor) { u_char msgbuf[SRSVP_MSG_SIZE], *cp = msgbuf; int afi = family2afi (srsvp->family); prefix_t *rsvp_hop; int lih; int truetype = type; if (type == SRSVP_MSG_RESV0 || type == SRSVP_MSG_RESV1) truetype = SRSVP_MSG_RESV; assert (truetype >= SRSVP_MSG_PATH && truetype <= SRSVP_MSG_RESV_TEAR); trace (TR_PACKET, neighbor->trace, "preparing %s srbit 0x%x\n", srsvp_pdus[truetype], srbit); trace (TR_PACKET, neighbor->trace, " session %a proto %d port %d\n", flow->destin, flow->proto, flow->dport); /* session class */ MRT_PUT_SHORT (SRSVP_OBJ_HDR_SIZE + afi2plen(afi) + 4, cp); MRT_PUT_BYTE (SRSVP_OBJ_SESSION, cp); MRT_PUT_BYTE (afi, cp); MRT_PUT_DATA (prefix_tochar (flow->destin), afi2plen (afi), cp); MRT_PUT_BYTE (flow->proto, cp); MRT_PUT_BYTE (flow->flags & 0xff, cp); MRT_PUT_SHORT (flow->dport, cp); if (type == SRSVP_MSG_PATH || type == SRSVP_MSG_PATH_TEAR) { rsvp_hop = neighbor->vif->prefix; lih = neighbor->lih; } else { rsvp_hop = neighbor->prefix; lih = neighbor->lih; } assert (rsvp_hop); assert (rsvp_hop->family == srsvp->family); trace (TR_PACKET, neighbor->trace, " rsvp_hop %a lih %d\n", rsvp_hop, lih); MRT_PUT_SHORT (SRSVP_OBJ_HDR_SIZE + afi2plen(afi) + 4, cp); MRT_PUT_BYTE (SRSVP_OBJ_RSVP_HOP, cp); MRT_PUT_BYTE (afi, cp); MRT_PUT_DATA (prefix_tochar (rsvp_hop), afi2plen (afi), cp); MRT_PUT_LONG (lih, cp); assert (flow->sender->family == srsvp->family); trace (TR_PACKET, neighbor->trace, " sender template %a port %d\n", flow->sender, flow->sport); MRT_PUT_SHORT (SRSVP_OBJ_HDR_SIZE + afi2plen(afi) + 4, cp); MRT_PUT_BYTE (SRSVP_OBJ_SENDERT, cp); MRT_PUT_BYTE (afi, cp); MRT_PUT_DATA (prefix_tochar (flow->sender), afi2plen (afi), cp); MRT_PUT_BYTE (0, cp); MRT_PUT_BYTE (0, cp); /* XXX */ MRT_PUT_SHORT (flow->sport, cp); if (type == SRSVP_MSG_RESV1 || (type == SRSVP_MSG_PATH && flow->sender_tspec)) { req_qos_t *req_qos; int class; if (type == SRSVP_MSG_RESV1) { req_qos = flow->req_qos; class = SRSVP_OBJ_FLOW_SPEC; trace_req_qos (TR_PACKET, neighbor->trace, req_qos, "flow-spec"); } else { req_qos = flow->sender_tspec; class = SRSVP_OBJ_SENDER_TSPEC; trace_req_qos (TR_PACKET, neighbor->trace, req_qos, "sender-tspec"); } assert (req_qos); MRT_PUT_SHORT (SRSVP_OBJ_HDR_SIZE + SRSVP_REQ_QOS_SIZE, cp); MRT_PUT_BYTE (class, cp); MRT_PUT_BYTE (3, cp); MRT_PUT_BYTE (req_qos->pri, cp); MRT_PUT_BYTE (req_qos->rsvd, cp); MRT_PUT_SHORT (req_qos->mtu, cp); MRT_PUT_LONG (req_qos->pps, cp); MRT_PUT_LONG (req_qos->sec, cp); MRT_PUT_LONG (req_qos->cd, cp); MRT_PUT_LONG (req_qos->cf, cp); MRT_PUT_LONG (req_qos->rdly, cp); MRT_PUT_LONG (req_qos->rfee, cp); } if (type == SRSVP_MSG_PATH_TEAR || type == SRSVP_MSG_RESV_TEAR) { char text[1024]; /* ERROR SPEC */ trace (TR_PACKET, neighbor->trace, " error-spec %s (0x%x)\n", srsvp_error_spec_string (flow->errcode, text), flow->errcode); MRT_PUT_SHORT (SRSVP_OBJ_HDR_SIZE + 4, cp); MRT_PUT_BYTE (SRSVP_OBJ_ERR_SPEC, cp); MRT_PUT_BYTE (3, cp); MRT_PUT_LONG (flow->errcode, cp); } return (srsvp_send_message (srsvp, neighbor, truetype, srbit, cp - msgbuf, msgbuf)); } typedef struct _error_specs_t { u_long errcode; char *text; } error_specs_t; static error_specs_t error_specs[] = { {SRSVP_MSG_ERR_UNREACH, "Unreachable Host"}, {SRSVP_MSG_ERR_BANDWIDTH, "Unavailable Bandwidth"}, {SRSVP_MSG_ERR_DELAY, "Unsatisfying Delay"}, {SRSVP_MSG_ERR_CHARGE, "Unsatisfying Charge"}, {0, NULL}, }; static char * srsvp_error_spec_string (u_long errcode, char *text) { static char stext[1024]; char *cp; int sep = '\0'; error_specs_t *table = error_specs; cp = (text)? text: stext; sprintf (cp, "Unknown code 0x%lx", errcode); if (errcode == 0) { strcpy (cp, "none"); } else { while (table->errcode) { if (BIT_TEST (errcode, table->errcode)) { if (sep) *cp++ = sep; strcpy (cp, table->text); cp += strlen (cp); sep = '/'; } table++; } } if (text) return (text); return (strdup (stext)); } static int srsvp_i_am_sender (srsvp_t *srsvp, srsvp_flow_t *flow) { srsvp_interface_t *vif; if (BIT_TEST (flow->flags, SRSVP_FLOWF_SENDER)) return (TRUE); LL_Iterate (srsvp->ll_srsvp_interfaces, vif) { if (address_equal (flow->sender, vif->prefix)) { trace (TR_TRACE, srsvp->trace, "flow %a port %d proto %d: " "I am the sender on %s at %a port %d\n", flow->destin, flow->dport, flow->proto, vif->interface->name, vif->prefix, flow->sport); BIT_SET (flow->flags, SRSVP_FLOWF_SENDER); return (TRUE); } } return (FALSE); } static int srsvp_accept_resv (srsvp_t *srsvp, int type, int srbit, srsvp_flow_t *flow, req_qos_t *req_qos, prefix_t *ssource, int lih, u_char *cp, int hdrlen, srsvp_neighbor_t *neighbor) { u_char *end = cp + hdrlen, *xp = cp; int len, class, afi; u_long errcode = 0; char text[1024]; srsvp_leaf_t *leaf = NULL; int change = 0; srsvp_flow_t *flow2; /* XXX FAQ_INFO */ while ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class != SRSVP_OBJ_FAQ_INFO) break; xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } } /* XXX PQC_INFO */ while ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class != SRSVP_OBJ_PQC_INFO) break; xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } } /* ERROR SPEC */ if ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class == SRSVP_OBJ_ERR_SPEC) { xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } if (type != SRSVP_MSG_RESV_TEAR) { trace (TR_ERROR, neighbor->trace, "error spec is not allowed in a resv message\n"); return (-1); } if (afi != 3) { trace (TR_ERROR, neighbor->trace, "unsupported type %d in error spec\n", afi); return (-1); } if (len != SRSVP_OBJ_HDR_SIZE + 4) { trace (TR_ERROR, neighbor->trace, "wrong len %d in error spec (must be %d)\n", len, 4 + 4); return (-1); } if (cp + 4 > xp) { trace (TR_ERROR, neighbor->trace, "inconsistency in length (%d short)\n", cp + 4 - xp); return (-1); } MRT_GET_LONG (errcode, cp); trace (TR_PACKET, neighbor->trace, " error-spec %s (0x%x)\n", srsvp_error_spec_string (errcode, text), errcode); } } if (xp != end) { trace (TR_ERROR, neighbor->trace, "wrong packet len (%d remains)\n", end - xp); return (-1); } if (type == SRSVP_MSG_RESV) type = (req_qos)? SRSVP_MSG_RESV1: SRSVP_MSG_RESV0; if (type == SRSVP_MSG_RESV0 && srsvp_i_am_sender (srsvp, flow)) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, srbit, flow, neighbor); return (1); } flow2 = flow; flow = srsvp_flow_find (srsvp, flow2); if (type == SRSVP_MSG_RESV_TEAR) { if (flow == NULL) { trace_flow (TR_PACKET, neighbor->trace, flow, "no flow"); return (0); } } if (flow == NULL) { flow = srsvp_flow_copy_add (srsvp, flow2); } flow->errcode = errcode; if (type == SRSVP_MSG_RESV_TEAR && srsvp_i_am_sender (srsvp, flow2)) { srsvp_destroy_flow (srsvp, flow); qif_notify (srsvp, flow, -1); return (1); } if (flow->ll_downstreams) { LL_Iterate (flow->ll_downstreams, leaf) { if (address_equal (leaf->neighbor->prefix, neighbor->prefix)) break; } } if (type == SRSVP_MSG_RESV_TEAR) { if (BIT_TEST (leaf->flags, SRSVP_LEAFF_READY)) { srsvp_notify_hqlip (srsvp, flow, leaf, OFF); qif_del_flow (srsvp, flow, leaf); } LL_Remove (flow->ll_downstreams, leaf); srsvp_destroy_leaf (srsvp, leaf); if (LL_GetCount (flow->ll_downstreams) <= 0) { srsvp_forward_msg (srsvp, SRSVP_MSG_RESV_TEAR, 0, flow, flow->upstream); srsvp_destroy_flow (srsvp, flow); } /* XXX in case of reducing pps */ return (0); } if (leaf == NULL) { leaf = New (srsvp_leaf_t); leaf->neighbor = neighbor; leaf->neighbor->lih = lih; /* XXX */ trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "add"); if (req_qos) { if (leaf->req_qos == NULL) leaf->req_qos = copy_req_qos (req_qos, NULL); if (flow->req_qos == NULL) { flow->req_qos = copy_req_qos (req_qos, NULL); change++; } else { /* merge req_qos in flow */ if (flow->req_qos->pps < req_qos->pps) { flow->req_qos->pps = req_qos->pps; change++; } } } LL_Add2 (flow->ll_downstreams, leaf); } else if (req_qos) { if (leaf->req_qos == NULL) leaf->req_qos = copy_req_qos (req_qos, NULL); else if (memcmp (leaf->req_qos, req_qos, sizeof (*req_qos)) != 0) { copy_req_qos (req_qos, leaf->req_qos); /* merge req_qos in flow */ /* XXX in case of reducing pps */ if (flow->req_qos->pps < req_qos->pps) { flow->req_qos->pps = req_qos->pps; change++; } } } if (!change) { /* short cut the resv message */ if (type == SRSVP_MSG_RESV0) { if (BIT_TEST (flow->flags, SRSVP_FLOWF_RESV0)) return (0); if (BIT_TEST (flow->flags, SRSVP_FLOWF_RESV1) || BIT_TEST (flow->flags, SRSVP_FLOWF_READY)) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, srbit, flow, neighbor); return (1); } } if (type == SRSVP_MSG_RESV1) { if (BIT_TEST (flow->flags, SRSVP_FLOWF_RESV1)) return (0); if (BIT_TEST (flow->flags, SRSVP_FLOWF_READY)) { BIT_RESET (leaf->flags, SRSVP_LEAFF_RESV1); BIT_SET (leaf->flags, SRSVP_LEAFF_READY); trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "ready"); srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, srbit, flow, neighbor); srsvp_notify_hqlip (srsvp, flow, leaf, ON); qif_add_flow (srsvp, flow, leaf); return (1); } } } if (type == SRSVP_MSG_RESV1 && srsvp_i_am_sender (srsvp, flow)) { BIT_RESET (leaf->flags, SRSVP_LEAFF_RESV1); BIT_SET (leaf->flags, SRSVP_LEAFF_READY); BIT_SET (flow->flags, SRSVP_FLOWF_READY); trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "ready"); srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, srbit, flow, neighbor); qif_notify (srsvp, flow, 0); srsvp_notify_hqlip (srsvp, flow, leaf, ON); qif_add_flow (srsvp, flow, leaf); return (1); } if (flow->upstream == NULL) { if (srsvp_resolv_upstream (srsvp, flow) < 0) { LL_Iterate (flow->ll_downstreams, leaf) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH_TEAR, 0, flow, leaf->neighbor); } srsvp_destroy_flow (srsvp, flow); return (0); } } switch (type) { case SRSVP_MSG_RESV0: trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "resv0"); srsvp_forward_msg (srsvp, SRSVP_MSG_RESV0, srbit, flow, flow->upstream); BIT_SET (leaf->flags, SRSVP_LEAFF_RESV0); BIT_SET (flow->flags, SRSVP_FLOWF_RESV0); break; case SRSVP_MSG_RESV1: trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "resv1"); srsvp_forward_msg (srsvp, SRSVP_MSG_RESV1, srbit, flow, flow->upstream); BIT_SET (leaf->flags, SRSVP_LEAFF_RESV1); BIT_SET (flow->flags, SRSVP_FLOWF_RESV1); break; } return (1); } static int srsvp_i_am_receiver (srsvp_t *srsvp, srsvp_flow_t *flow) { srsvp_interface_t *vif; if (BIT_TEST (flow->flags, SRSVP_FLOWF_RECVER)) return (TRUE); LL_Iterate (srsvp->ll_srsvp_interfaces, vif) { if (address_equal (flow->destin, vif->prefix)) { trace (TR_TRACE, srsvp->trace, "flow %a port %d proto %d: " "I am the receiver on %s at %a port %d\n", flow->destin, flow->dport, flow->proto, vif->interface->name, vif->prefix, flow->dport); BIT_SET (flow->flags, SRSVP_FLOWF_RECVER); return (TRUE); } } return (FALSE); } static int srsvp_accept_path (srsvp_t *srsvp, int type, int srbit, srsvp_flow_t *flow, req_qos_t *req_qos, prefix_t *ssource, int lih, u_char *cp, int hdrlen, srsvp_neighbor_t *neighbor) { u_char *end = cp + hdrlen, *xp = cp; int len, class, afi; char text[1024]; srsvp_leaf_t *leaf; srsvp_flow_t *flow2; srsvp_neighbor_t *neighbor2; u_long errcode = 0; /* XXX POLICY_DATA */ while ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class != SRSVP_OBJ_POLICYD) break; xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } } /* XXX FAQ_INFO */ while ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class != SRSVP_OBJ_FAQ_INFO) break; xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } } /* XXX PQC_INFO */ while ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class != SRSVP_OBJ_PQC_INFO) break; xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } } /* ERROR SPEC */ if ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class == SRSVP_OBJ_ERR_SPEC) { xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } if (type == SRSVP_MSG_PATH) { trace (TR_ERROR, neighbor->trace, "error spec is not allowed in a path message\n"); return (-1); } if (afi != 3) { trace (TR_ERROR, neighbor->trace, "unsupported type %d in error spec\n", afi); return (-1); } if (len != SRSVP_OBJ_HDR_SIZE + 4) { trace (TR_ERROR, neighbor->trace, "wrong len %d in error spec (must be %d)\n", len, 4 + 4); return (-1); } if (cp + 4 > xp) { trace (TR_ERROR, neighbor->trace, "inconsistency in length (%d short)\n", cp + 4 - xp); return (-1); } MRT_GET_LONG (errcode, cp); trace (TR_PACKET, neighbor->trace, " error-spec %s (0x%x)\n", srsvp_error_spec_string (errcode, text), errcode); } } if (xp != end) { trace (TR_ERROR, neighbor->trace, "wrong packet len (%d remains)\n", end - xp); return (-1); } /* XXX req_qos is sender tspec -- optional */ flow2 = flow; flow = srsvp_flow_find (srsvp, flow2); if (type == SRSVP_MSG_PATH_TEAR) { if (flow == NULL) { trace_flow (TR_PACKET, neighbor->trace, flow2, "no flow"); return (0); } flow->errcode = errcode; if (BIT_TEST (flow->flags, SRSVP_FLOWF_RECVER)) { qif_notify (srsvp, flow, -1); srsvp_destroy_flow (srsvp, flow); return (0); } /* XXX need to check who caused this */ LL_Iterate (flow->ll_downstreams, leaf) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH_TEAR, srbit, flow, leaf->neighbor); if (BIT_TEST (leaf->flags, SRSVP_LEAFF_READY)) { srsvp_notify_hqlip (srsvp, flow, leaf, OFF); qif_del_flow (srsvp, flow, leaf); } } srsvp_destroy_flow (srsvp, flow); return (0); } if (flow == NULL) { if (srsvp_i_am_receiver (srsvp, flow2)) { if (srsvp_resolv_upstream (srsvp, flow2) < 0) { return (0); } if (flow2->sender_tspec == NULL) { trace (TR_ERROR, neighbor->trace, "no sender-tspec\n"); return (0); } flow = srsvp_flow_copy_add (srsvp, flow2); flow->req_qos = flow->sender_tspec; flow->sender_tspec = NULL; trace_flow (TR_TRACE, neighbor->trace, flow, "resv1"); srsvp_forward_msg (srsvp, SRSVP_MSG_RESV1, 0, flow, flow->upstream); BIT_SET (flow->flags, SRSVP_FLOWF_RESV1); return (1); } without_resv0: flow = flow2; trace (TR_TRACE, neighbor->trace, "going without resv0 (%s)\n", (flow)? "no downstreams": "no flow"); neighbor2 = srsvp_get_upstream (srsvp, flow->destin, NULL, &flow->errcode); if (neighbor2 != NULL) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, srbit, flow, neighbor2); } else { srsvp_forward_msg (srsvp, SRSVP_MSG_RESV_TEAR, 0, flow, neighbor); } return (0); } if (flow->upstream != neighbor) { assert (flow->upstream); trace (TR_ERROR, neighbor->trace, "not upstream (must be %a)\n", flow->upstream->prefix); srsvp_forward_msg (srsvp, SRSVP_MSG_PATH_TEAR, 0, flow, neighbor); return (0); } if (BIT_TEST (flow->flags, SRSVP_FLOWF_RECVER)) { if (BIT_TEST (flow->flags, SRSVP_FLOWF_RESV1)) { BIT_SET (flow->flags, SRSVP_FLOWF_READY); qif_notify (srsvp, flow, 0); trace_flow (TR_TRACE, neighbor->trace, flow, "ready"); } else { if (srsvp_resolv_upstream (srsvp, flow) < 0) { srsvp_destroy_flow (srsvp, flow); return (0); } trace_flow (TR_TRACE, neighbor->trace, flow, "resv1"); srsvp_forward_msg (srsvp, SRSVP_MSG_RESV1, 0, flow, flow->upstream); BIT_SET (flow->flags, SRSVP_FLOWF_RESV1); } return (1); } if (flow->ll_downstreams == NULL || LL_GetCount (flow->ll_downstreams) <= 0) { /* no down streams */ goto without_resv0; } if (BIT_TEST (flow->flags, SRSVP_FLOWF_RESV1)) { BIT_SET (flow->flags, SRSVP_FLOWF_READY); BIT_RESET (flow->flags, SRSVP_FLOWF_RESV1); } LL_Iterate (flow->ll_downstreams, leaf) { if (BIT_TEST (leaf->flags, SRSVP_LEAFF_RESV1)) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, srbit, flow, leaf->neighbor); BIT_RESET (leaf->flags, SRSVP_LEAFF_RESV1); if (BIT_TEST (leaf->flags, SRSVP_LEAFF_READY)) { trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "change"); /* XXX no way to update */ /* srsvp_notify_hqlip (srsvp, flow, leaf, OFF); srsvp_notify_hqlip (srsvp, flow, leaf, ON); */ } else { BIT_SET (leaf->flags, SRSVP_LEAFF_READY); trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "ready"); srsvp_notify_hqlip (srsvp, flow, leaf, ON); } qif_add_flow (srsvp, flow, leaf); } else { srsvp_leaf_t *prev; srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, srbit, flow, leaf->neighbor); prev = LL_GetPrev (flow->ll_downstreams, leaf); LL_Remove (flow->ll_downstreams, leaf); srsvp_destroy_leaf (srsvp, leaf); leaf = prev; if (LL_GetCount (flow->ll_downstreams) <= 0) { srsvp_destroy_flow (srsvp, flow); /* must exit since it was destroyed */ break; } } } return (1); } static u_int srsvp_flow_hash_fn (srsvp_flow_t *flow, u_int size) { int val = 0; val += ip_hash_fn (flow->destin, size); val += flow->dport; val += flow->proto; val += ip_hash_fn (flow->sender, size); val += flow->sport; val = val % size; return (val); } static int srsvp_flow_lookup_fn (srsvp_flow_t *a, srsvp_flow_t *b) { if (a->dport != b->dport) return (FALSE); if (a->proto != b->proto) return (FALSE); if (!address_equal (a->destin, b->destin)) return (FALSE); if (a->sport != b->sport) return (FALSE); if (!address_equal (a->sender, b->sender)) return (FALSE); return (TRUE); } static srsvp_flow_t * srsvp_flow_create (srsvp_t *srsvp, prefix_t *destin, int proto, int dport, prefix_t *sender, int sport, req_qos_t *req_qos, u_long flags, srsvp_flow_t *flow) { if (flow == NULL) { flow = New (srsvp_flow_t); flow->destin = Ref_Prefix (destin); flow->sender = Ref_Prefix (sender); if (req_qos) { flow->req_qos = copy_req_qos (req_qos, NULL); } else { flow->req_qos = NULL; } flow->ll_downstreams = LL_Create (0); } else { /* assuming a static memory */ memset (flow, 0, sizeof (*flow)); flow->destin = destin; flow->sender = sender; flow->req_qos = req_qos; flow->ll_downstreams = NULL; } flow->dport = dport; flow->proto = proto; flow->flags = flags; flow->errcode = 0; flow->sport = sport; flow->upstream = NULL; return (flow); } static void srsvp_destroy_flow2 (srsvp_t *srsvp, srsvp_flow_t *flow) { pthread_mutex_lock (&srsvp->flows->mutex_lock); HASH_Remove (srsvp->flows->table, flow); pthread_mutex_unlock (&srsvp->flows->mutex_lock); trace_flow (TR_TRACE, srsvp->trace, flow, "del"); Deref_Prefix (flow->destin); Deref_Prefix (flow->sender); if (flow->req_qos) Delete (flow->req_qos); if (flow->sender_tspec) Delete (flow->sender_tspec); if (flow->ll_downstreams) { srsvp_leaf_t *leaf; LL_Iterate (flow->ll_downstreams, leaf) { srsvp_destroy_leaf (srsvp, leaf); } LL_Destroy (flow->ll_downstreams); } Delete (flow); } srsvp_flow_t * srsvp_flow_find (srsvp_t *srsvp, srsvp_flow_t *flow) { srsvp_flow_t *flow2; if (srsvp->flows == NULL) return (NULL); pthread_mutex_lock (&srsvp->flows->mutex_lock); flow2 = HASH_Lookup (srsvp->flows->table, flow); pthread_mutex_unlock (&srsvp->flows->mutex_lock); if (flow2 && BIT_TEST (flow2->flags, SRSVP_FLOWF_DELETED)) { /* the end was delayed to now */ srsvp_destroy_flow2 (srsvp, flow2); flow2 = NULL; } return (flow2); } static srsvp_flow_t * srsvp_flow_copy_add (srsvp_t *srsvp, srsvp_flow_t *flow) { srsvp_flow_t *nflow; if (srsvp->flows == NULL) { srsvp->flows = New (mrt_hash_table_t); pthread_mutex_init (&srsvp->flows->mutex_lock, NULL); } nflow = srsvp_flow_create (srsvp, flow->destin, flow->proto, flow->dport, flow->sender, flow->sport, flow->req_qos, flow->flags, NULL); trace_flow (TR_TRACE, srsvp->trace, nflow, "add"); if (nflow->req_qos) trace_req_qos (TR_TRACE, srsvp->trace, nflow->req_qos, "flow-spec"); if (flow->sender_tspec) { nflow->sender_tspec = copy_req_qos (flow->sender_tspec, NULL); trace_req_qos (TR_TRACE, srsvp->trace, nflow->sender_tspec, "sender-tspec"); } if (flow->ll_downstreams) { srsvp_leaf_t *leaf, *nleaf; LL_Iterate (flow->ll_downstreams, leaf) { nleaf = New (srsvp_leaf_t); nleaf->neighbor = leaf->neighbor; nleaf->flags = leaf->flags; trace_neighbor (TR_TRACE, srsvp->trace, leaf->neighbor, "add"); if (leaf->req_qos) { nleaf->req_qos = copy_req_qos (leaf->req_qos, NULL); trace_req_qos (TR_TRACE, srsvp->trace, leaf->req_qos, "flow-spec"); } LL_Add2 (nflow->ll_downstreams, nleaf); } } nflow->upstream = flow->upstream; pthread_mutex_lock (&srsvp->flows->mutex_lock); if (srsvp->flows->table == NULL) { srsvp->flows->table = HASH_Create (SRSVP_FLOW_HASH_SIZE, HASH_EmbeddedKey, True, HASH_KeyOffset, 0, HASH_LookupFunction, srsvp_flow_lookup_fn, HASH_HashFunction, srsvp_flow_hash_fn, 0); } HASH_Insert (srsvp->flows->table, nflow); pthread_mutex_unlock (&srsvp->flows->mutex_lock); return (nflow); } static void srsvp_destroy_leaf (srsvp_t *srsvp, srsvp_leaf_t *leaf) { trace_neighbor (TR_TRACE, srsvp->trace, leaf->neighbor, "del"); if (leaf->req_qos) Delete (leaf->req_qos); Delete (leaf); } static void srsvp_destroy_flow (srsvp_t *srsvp, srsvp_flow_t *flow) { BIT_SET (flow->flags, SRSVP_FLOWF_DELETED); } static int srsvp_process_pdu (srsvp_t *srsvp, srsvp_neighbor_t *neighbor) { int version, hdrflags, sessionf, senderf, type, ttl, srbit, hdrlen; u_char *cp, *end, *xp; int class, len, proto, dafi, safi, dport, lih, sport = 0; prefix_t sdestin, ssource, ssender, *sender = NULL; u_char addr[16]; srsvp_flow_t sflow, *flow = NULL; req_qos_t rqos, *req_qos = NULL; int ret = 0; assert (neighbor); assert (neighbor->packet); neighbor->num_packets_recv++; cp = neighbor->packet; SRSVP_GET_HEADER (version, hdrflags, type, ttl, srbit, hdrlen, cp); end = neighbor->packet + hdrlen; xp = cp; if (type >= SRSVP_MSG_PATH && type <= SRSVP_MSG_RESV_TEAR) { trace (TR_PACKET, neighbor->trace, "recv %s version %d flags 0x%x ttl %d srbit 0x%x " "(%d bytes)\n", srsvp_pdus[type], version, hdrflags, ttl, srbit, hdrlen); } if (hdrlen < SRSVP_MSG_HDR_SIZE) { trace (TR_ERROR, neighbor->trace, "recv a message with too short length %d\n", hdrlen); return (-1); } if (version != SRSVP_MSG_VERSION) { trace (TR_ERROR, neighbor->trace, "recv a message with unsupported version %d\n", version); return (-1); } /* session class */ if ((cp = xp) + SRSVP_OBJ_HDR_SIZE > end) { trace (TR_ERROR, neighbor->trace, "recv a message with too short len %d to get session\n", hdrlen); return (-1); } MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (dafi, cp); if (class != SRSVP_OBJ_SESSION) { trace (TR_ERROR, neighbor->trace, "session is missing (len %d class %d afi %d)\n", len, class, dafi); return (-1); } xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, dafi); return (-1); } #ifdef HAVE_IPV6 if (dafi == AFI_IP6) { /* OK */ } else #endif /* HAVE_IPV6 */ if (dafi == AFI_IP) { /* OK */ } else { trace (TR_ERROR, neighbor->trace, "unsupported afi %d in session\n", dafi); return (-1); } if (len != SRSVP_OBJ_HDR_SIZE + afi2plen (dafi) + 4) { trace (TR_ERROR, neighbor->trace, "wrong len %d in session (must be %d)\n", len, afi2plen (dafi) + 4); return (-1); } if (cp + afi2plen (dafi) + 4 > xp) { trace (TR_ERROR, neighbor->trace, "inconsistency in length (%d short)\n", cp + afi2plen (dafi) + 4 - xp); return (-1); } MRT_GET_DATA (addr, afi2plen (dafi), cp); MRT_GET_BYTE (proto, cp); MRT_GET_BYTE (sessionf, cp); MRT_GET_SHORT (dport, cp); New_Prefix2 (afi2family (dafi), addr, -1, &sdestin); trace (TR_PACKET, neighbor->trace, " session %a proto %d port %d\n", &sdestin, proto, dport); /* session rsvp_hop */ if ((cp = xp) + SRSVP_OBJ_HDR_SIZE > end) { trace (TR_ERROR, neighbor->trace, "recv a message with too short len %d to get rsvp_hop\n", hdrlen); return (-1); } MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (safi, cp); if (class != SRSVP_OBJ_RSVP_HOP) { trace (TR_ERROR, neighbor->trace, "rsvp_hop is missing (len %d class %d afi %d)\n", len, class, safi); return (-1); } xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, safi); return (-1); } #ifdef HAVE_IPV6 if (safi == AFI_IP6) { /* OK */ } else #endif /* HAVE_IPV6 */ if (safi == AFI_IP) { /* OK */ } else { trace (TR_ERROR, neighbor->trace, "unsupported afi %d in rsvp_hop\n", safi); return (-1); } if (len != SRSVP_OBJ_HDR_SIZE + afi2plen (safi) + 4) { trace (TR_ERROR, neighbor->trace, "wrong len %d in rsvp_hop (must be %d)\n", len, afi2plen (safi) + 4); return (-1); } if (cp + afi2plen (safi) + 4 > xp) { trace (TR_ERROR, neighbor->trace, "inconsistency in length (%d short)\n", cp + afi2plen (safi) + 4 - xp); return (-1); } if (dafi != safi) { trace (TR_ERROR, neighbor->trace, "inconsistency in afi of session %d and rsvp_hop %d\n", dafi, safi); return (-1); } MRT_GET_DATA (addr, afi2plen (safi), cp); MRT_GET_LONG (lih, cp); New_Prefix2 (afi2family (safi), addr, -1, &ssource); trace (TR_PACKET, neighbor->trace, " rsvp_hop %a lih %d\n", &ssource, lih); while ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { int afi; MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class != SRSVP_OBJ_SENDERT) { break; } xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, dafi); return (-1); } #ifdef HAVE_IPV6 if (afi == AFI_IP6) { /* OK */ } else #endif /* HAVE_IPV6 */ if (afi == AFI_IP) { /* OK */ } else { trace (TR_ERROR, neighbor->trace, "unsupported afi %d in sender tamplate\n", afi); return (-1); } if (len != SRSVP_OBJ_HDR_SIZE + afi2plen (afi) + 4) { trace (TR_ERROR, neighbor->trace, "wrong len %d in sender tamplate (must be %d)\n", len, afi2plen (afi) + 4); return (-1); } if (cp + afi2plen (afi) + 4 > xp) { trace (TR_ERROR, neighbor->trace, "inconsistency in length (%d short)\n", cp + afi2plen (afi) + 4 - xp); return (-1); } if (sender != NULL) { trace (TR_WARN, neighbor->trace, "only one sender allowed in this version\n"); cp = cp + afi2plen (afi) + 4; } else { MRT_GET_DATA (addr, afi2plen (afi), cp); cp += 1; MRT_GET_BYTE (senderf, cp); MRT_GET_SHORT (sport, cp); New_Prefix2 (afi2family (afi), addr, -1, &ssender); sender = &ssender; trace (TR_PACKET, neighbor->trace, " sender template %a flags 0x%x port %d\n", sender, senderf, sport); } } if (sender == NULL) { trace (TR_ERROR, neighbor->trace, "at least one sender must be specified in a message\n"); return (-1); } flow = srsvp_flow_create (srsvp, &sdestin, proto, dport, &ssender, sport, NULL, 0, &sflow); if ((cp = xp) + SRSVP_OBJ_HDR_SIZE <= end) { int afi; char *text = "????"; MRT_GET_SHORT (len, cp); MRT_GET_BYTE (class, cp); MRT_GET_BYTE (afi, cp); if (class == SRSVP_OBJ_FLOW_SPEC || class == SRSVP_OBJ_SENDER_TSPEC) { xp += len; if (xp > end) { trace (TR_ERROR, neighbor->trace, "wrong object len %d (exceeds %d) class %d type %d\n", len, xp - end, class, afi); return (-1); } req_qos = &rqos; switch (type) { case SRSVP_MSG_PATH: case SRSVP_MSG_PATH_TEAR: flow->sender_tspec = req_qos; text = "sender-tspec"; if (class == SRSVP_OBJ_FLOW_SPEC) { trace (TR_ERROR, neighbor->trace, "flow spec is not allowed in a path message\n"); return (-1); } break; case SRSVP_MSG_RESV: case SRSVP_MSG_RESV_TEAR: flow->req_qos = req_qos; text = "flow-spec"; if (class == SRSVP_OBJ_SENDER_TSPEC) { trace (TR_ERROR, neighbor->trace, "sender tspec is not allowed in a resv message\n"); return (-1); } break; } if (afi != 3) { trace (TR_ERROR, neighbor->trace, "unsupported type %d in flow spec or sender tspec\n", afi); return (-1); } if (len != SRSVP_OBJ_HDR_SIZE + SRSVP_REQ_QOS_SIZE) { trace (TR_ERROR, neighbor->trace, "wrong len %d in flow spec or sender tspec " "(must be %d)\n", len, SRSVP_OBJ_HDR_SIZE + SRSVP_REQ_QOS_SIZE); return (-1); } if (cp + SRSVP_REQ_QOS_SIZE > xp) { trace (TR_ERROR, neighbor->trace, "inconsistency in length (%d short)\n", cp + SRSVP_REQ_QOS_SIZE - xp); return (-1); } MRT_GET_BYTE (req_qos->pri, cp); MRT_GET_BYTE (req_qos->rsvd, cp); MRT_GET_SHORT (req_qos->mtu, cp); MRT_GET_LONG (req_qos->pps, cp); MRT_GET_LONG (req_qos->sec, cp); MRT_GET_LONG (req_qos->cd, cp); MRT_GET_LONG (req_qos->cf, cp); MRT_GET_LONG (req_qos->rdly, cp); MRT_GET_LONG (req_qos->rfee, cp); trace_req_qos (TR_PACKET, neighbor->trace, req_qos, text); } } switch (type) { case SRSVP_MSG_PATH_TEAR: case SRSVP_MSG_PATH: ret = srsvp_accept_path (srsvp, type, srbit, flow, req_qos, &ssource, lih, xp, end - xp, neighbor); break; case SRSVP_MSG_RESV_TEAR: case SRSVP_MSG_RESV: ret = srsvp_accept_resv (srsvp, type, srbit, flow, req_qos, &ssource, lih, xp, end - xp, neighbor); break; default: trace (TR_ERROR, neighbor->trace, "recv unknown message type %d (%d bytes)\n", type, len); ret = -1; } return (ret); } static void srsvp_neighbor_start (srsvp_t *srsvp, srsvp_neighbor_t *neighbor) { time (&neighbor->utime); select_add_fd_event ("srsvp_get_pdu", neighbor->sockfd, SELECT_READ, 1 /* on */, neighbor->schedule, (event_fn_t) srsvp_get_pdu, 2, srsvp, neighbor); #ifndef HAVE_LIBPTHREAD select_add_fd_event ("srsvp_flush_queue", neighbor->sockfd, SELECT_WRITE, 0 /* off */, neighbor->schedule, (event_fn_t) srsvp_flush_queue, 2, srsvp, neighbor); #endif /* HAVE_LIBPTHREAD */ neighbor->num_packets_sent = 0; neighbor->num_packets_recv = 0; neighbor->num_session_up++; BIT_SET (neighbor->flags, SRSVP_NEIGHBOR_CONNECTED); Timer_Turn_ON (neighbor->keep_alive); } static srsvp_neighbor_t * srsvp_get_upstream (srsvp_t *srsvp, prefix_t *sender, req_qos_t *req_qos, u_long *errcode_p) { hqlip_t *hqlip = RICD->hqlip; link_qos_t lqos; int metric; srsvp_interface_t *vif; srsvp_neighbor_t *neighbor; hqlip_neighbor_t *hqlip_neighbor; assert (errcode_p); #ifdef HAVE_IPV6 if (sender->family == AF_INET6) hqlip = RICD6->hqlip; #endif /* HAVE_IPV6 */ hqlip_neighbor = hqlip_find_nexthop (hqlip, hqlip->root, sender, req_qos, &lqos, &metric, errcode_p); if (hqlip_neighbor == NULL) { char text[1024]; trace (TR_TRACE, srsvp->trace, "hqlip neighbor not found for sender %a (%s)\n", sender, srsvp_error_spec_string (*errcode_p, text)); return (NULL); } LL_Iterate (srsvp->ll_srsvp_interfaces, vif) { LL_Iterate (vif->ll_neighbors, neighbor) { if (BIT_TEST (neighbor->flags, SRSVP_NEIGHBOR_DELETED)) continue; if (address_equal (hqlip_neighbor->prefix, neighbor->prefix)) { trace (TR_TRACE, srsvp->trace, "srsvp neighbor %a found on %s for sender %a\n", neighbor->prefix, neighbor->vif->interface->name, sender); return (neighbor); } } } trace (TR_TRACE, srsvp->trace, "neighbor not found for sender %a (no srsvp neighbor for %a)\n", sender, hqlip_neighbor->prefix); *errcode_p = SRSVP_MSG_ERR_UNREACH; return (NULL); } static int srsvp_resolv_upstream (srsvp_t *srsvp, srsvp_flow_t *flow) { srsvp_neighbor_t *neighbor; neighbor = srsvp_get_upstream (srsvp, flow->sender, flow->req_qos, &flow->errcode); if (neighbor == NULL) { char text[1024]; trace (TR_TRACE, srsvp->trace, "flow %a port %d proto %d: upstream not found (%s)\n", flow->destin, flow->dport, flow->proto, srsvp_error_spec_string (flow->errcode, text)); flow->upstream = NULL; return (-1); } trace (TR_TRACE, srsvp->trace, "flow %a port %d proto %d: upstream %a found on %s (%s)\n", flow->destin, flow->dport, flow->proto, neighbor->prefix, neighbor->vif->interface->name, (flow->upstream == NULL)? "new": (flow->upstream != neighbor)? "change": "same"); flow->upstream = neighbor; return (1); } /* called from routing socket receiver */ void srsvp_flow_request_by_app (srsvp_t *srsvp, srsvp_flow_t *flow, int cmd) { prefix_t *nexthop; u_long errcode; srsvp_neighbor_t *neighbor; char text[256] = "app-req recv"; prefix_t *rvp = NULL; assert (cmd == 'S' || cmd == 'R'); assert (flow); nexthop = flow->sender; if (cmd == 'S') { nexthop = flow->destin; strcpy (text, "app-req send"); } trace_flow (TR_TRACE, srsvp->trace, flow, text); trace_req_qos (TR_TRACE, srsvp->trace, flow->req_qos, "app-req"); if (flow->req_qos->pps == 0) { if ((flow = srsvp_flow_find (srsvp, flow)) == NULL) { trace (TR_ERROR, srsvp->trace, "no flow\n"); return; } if (BIT_TEST (flow->flags, SRSVP_FLOWF_SENDER)) { srsvp_leaf_t *leaf; if (flow->ll_downstreams) { LL_Iterate (flow->ll_downstreams, leaf) { if (!BIT_TEST (leaf->neighbor->vif->interface->flags, IFF_LOOPBACK)) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH_TEAR, 0, flow, leaf->neighbor); } if (BIT_TEST (leaf->flags, SRSVP_LEAFF_READY)) { srsvp_notify_hqlip (srsvp, flow, leaf, OFF); qif_del_flow (srsvp, flow, leaf); } } } } else { if (flow->upstream) srsvp_forward_msg (srsvp, SRSVP_MSG_RESV_TEAR, 0, flow, flow->upstream); } srsvp_destroy_flow (srsvp, flow); return; } if (prefix_is_multicast (flow->destin)) { if (cmd == 'S') { trace (TR_ERROR, srsvp->trace, "no send multicast\n"); qif_notify (srsvp, flow, -1); return; } if ((rvp = ricd_get_rvp (flow->destin)) == NULL) { trace (TR_ERROR, srsvp->trace, "rvp for %a not found\n", flow->destin); qif_notify (srsvp, flow, -1); return; } trace (TR_TRACE, srsvp->trace, "rvp for %a is %a\n", flow->destin, rvp); flow->sender = rvp; nexthop = rvp; } neighbor = srsvp_get_upstream (srsvp, nexthop, flow->req_qos, &errcode); if (neighbor == NULL) { char text[1024]; trace (TR_ERROR, srsvp->trace, "next-hop for %a not found (%s)\n", nexthop, srsvp_error_spec_string (errcode, text)); qif_notify (srsvp, flow, -1); return; } if (srsvp_flow_find (srsvp, flow) != NULL) { trace (TR_ERROR, srsvp->trace, "flow exist\n"); qif_notify (srsvp, flow, -1); return; } flow = srsvp_flow_copy_add (srsvp, flow); if (cmd == 'S') { srsvp_leaf_t *leaf; leaf = New (srsvp_leaf_t); leaf->neighbor = neighbor; leaf->neighbor->lih = 0; /* XXX */ LL_Add2 (flow->ll_downstreams, leaf); trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "add"); flow->flags = SRSVP_FLOWF_SENDER; if (BIT_TEST (neighbor->vif->interface->flags, IFF_LOOPBACK)) { BIT_SET (flow->flags, SRSVP_FLOWF_READY); BIT_SET (leaf->flags, SRSVP_LEAFF_READY); leaf->req_qos = copy_req_qos (flow->req_qos, NULL); srsvp_notify_hqlip (srsvp, flow, leaf, ON); qif_add_flow (srsvp, flow, leaf); qif_notify (srsvp, flow, 0); } else { leaf->req_qos = NULL; flow->sender_tspec = flow->req_qos; flow->req_qos = NULL; srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, 0, flow, neighbor); } } else { flow->upstream = neighbor; flow->flags = SRSVP_FLOWF_RECVER; if (BIT_TEST (neighbor->vif->interface->flags, IFF_LOOPBACK)) { srsvp_leaf_t *leaf; leaf = New (srsvp_leaf_t); leaf->neighbor = neighbor; leaf->neighbor->lih = 0; /* XXX */ LL_Add2 (flow->ll_downstreams, leaf); trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "add"); BIT_SET (flow->flags, SRSVP_FLOWF_READY); BIT_SET (leaf->flags, SRSVP_LEAFF_READY); leaf->req_qos = copy_req_qos (flow->req_qos, NULL); srsvp_notify_hqlip (srsvp, flow, leaf, ON); qif_add_flow (srsvp, flow, leaf); qif_notify (srsvp, flow, 0); } else { srsvp_forward_msg (srsvp, SRSVP_MSG_RESV0, 0, flow, neighbor); } } } /* "flow (send|recv) %a port %d (udp|tcp) %sreq-qos" */ int srsvp_flow_request_by_user (uii_connection_t *uii, char *send_s, prefix_t *destin, int dport, char *proto_s, char *req_qos_s) { int proto = IPPROTO_TCP; config_req_qos_t *config_req_qos; req_qos_t *req_qos = NULL; u_long errcode; srsvp_flow_t sflow, *flow; srsvp_neighbor_t *neighbor; srsvp_t *srsvp = RICD->srsvp; prefix_t sprefix; prefix_t *rvp = NULL; #ifdef HAVE_IPV6 if (destin->family == AF_INET6) srsvp = RICD6->srsvp; #endif /* HAVE_IPV6 */ if (strcasecmp (proto_s, "udp") == 0) proto = IPPROTO_UDP; else if (strcasecmp (proto_s, "tcp") == 0) proto = IPPROTO_TCP; LL_Iterate (CONFIG_RICD->ll_config_req_qoses, config_req_qos) { if (BIT_TEST (config_req_qos->flags, CONFIG_QOS_DELETED)) break; if (strcasecmp (config_req_qos->name, req_qos_s) == 0) { req_qos = config_req_qos->req_qos; break; } } if (req_qos == NULL) { config_notice (TR_ERROR, uii, "req-qos %s not found\n", req_qos_s); Deref_Prefix (destin); Delete (send_s); Delete (proto_s); Delete (req_qos_s); return (-1); } if (prefix_is_multicast (destin)) { if (strcasecmp (send_s, "send") == 0) { config_notice (TR_ERROR, uii, "no send multicast\n"); Deref_Prefix (destin); Delete (send_s); Delete (proto_s); Delete (req_qos_s); return (-1); } if ((rvp = ricd_get_rvp (destin)) == NULL) { config_notice (TR_ERROR, uii, "rvp for %a not found\n", destin); Deref_Prefix (destin); Delete (send_s); Delete (proto_s); Delete (req_qos_s); return (-1); } config_notice (TR_TRACE, uii, "rvp for %a is %a\n", destin, rvp); } neighbor = srsvp_get_upstream (srsvp, (rvp)? rvp: destin, req_qos, &errcode); if (neighbor == NULL) { char text[1024]; config_notice (TR_ERROR, uii, "%s %a not found (%s)\n", (rvp)? "rvp": "detination", (rvp)? rvp: destin, srsvp_error_spec_string (errcode, text)); Deref_Prefix (destin); Delete (send_s); Delete (proto_s); Delete (req_qos_s); return (-1); } /* hash function looks at prefix length, too */ New_Prefix2 (neighbor->vif->prefix->family, prefix_tochar (neighbor->vif->prefix), -1, &sprefix); if (strcasecmp (send_s, "send") == 0) { srsvp_flow_create (srsvp, destin, proto, dport, &sprefix, dport /* XXX */, req_qos, SRSVP_FLOWF_SENDER, &sflow); } else if (rvp) { srsvp_flow_create (srsvp, destin, proto, dport, rvp, dport /* XXX */, req_qos, SRSVP_FLOWF_RECVER, &sflow); } else { srsvp_flow_create (srsvp, &sprefix, proto, dport, destin, dport /* XXX */, req_qos, SRSVP_FLOWF_RECVER, &sflow); } if ((flow = srsvp_flow_find (srsvp, &sflow)) != NULL) { config_notice (TR_ERROR, uii, "you can not change an existing one\n"); Deref_Prefix (destin); Delete (send_s); Delete (proto_s); Delete (req_qos_s); return (-1); } flow = srsvp_flow_copy_add (srsvp, &sflow); if (strcasecmp (send_s, "send") == 0) { srsvp_leaf_t *leaf; leaf = New (srsvp_leaf_t); leaf->neighbor = neighbor; leaf->neighbor->lih = 0; /* XXX */ leaf->req_qos = NULL; flow->sender_tspec = flow->req_qos; flow->req_qos = NULL; LL_Add2 (flow->ll_downstreams, leaf); trace_neighbor (TR_TRACE, neighbor->trace, leaf->neighbor, "add"); srsvp_forward_msg (srsvp, SRSVP_MSG_PATH, 0, flow, neighbor); srsvp_destroy_flow (srsvp, flow); } else { flow->upstream = neighbor; srsvp_forward_msg (srsvp, SRSVP_MSG_RESV0, 0, flow, neighbor); } Deref_Prefix (destin); Delete (send_s); Delete (proto_s); Delete (req_qos_s); return (1); } int srsvp_no_flow_request_by_user (uii_connection_t *uii, char *ip, int num) { srsvp_t *srsvp = RICD->srsvp; srsvp_flow_t *flow; int count = 1; #ifdef HAVE_IPV6 if (strcasecmp (ip, "ipv6") == 0) srsvp = RICD6->srsvp; #endif /* HAVE_IPV6 */ Delete (ip); if (srsvp->flows == NULL) return (0); pthread_mutex_lock (&srsvp->flows->mutex_lock); HASH_Iterate (srsvp->flows->table, flow) { if (count == num) break; } pthread_mutex_unlock (&srsvp->flows->mutex_lock); if (flow == 0) { config_notice (TR_ERROR, uii, "no such flow: %d\n", num); return (0); } if (BIT_TEST (flow->flags, SRSVP_FLOWF_SENDER)) { srsvp_leaf_t *leaf; if (flow->ll_downstreams) { LL_Iterate (flow->ll_downstreams, leaf) { srsvp_forward_msg (srsvp, SRSVP_MSG_PATH_TEAR, 0, flow, leaf->neighbor); if (BIT_TEST (leaf->flags, SRSVP_LEAFF_READY)) { srsvp_notify_hqlip (srsvp, flow, leaf, OFF); qif_del_flow (srsvp, flow, leaf); } } } } else { if (flow->upstream) srsvp_forward_msg (srsvp, SRSVP_MSG_RESV_TEAR, 0, flow, flow->upstream); } srsvp_destroy_flow (srsvp, flow); return (1); } int srsvp_show_flows (uii_connection_t *uii, char *ip) { srsvp_t *srsvp = RICD->srsvp; srsvp_flow_t *flow; int count = 1; #ifdef HAVE_IPV6 if (strcasecmp (ip, "ipv6") == 0) srsvp = RICD6->srsvp; #endif /* HAVE_IPV6 */ Delete (ip); if (srsvp->flows == NULL) return (0); pthread_mutex_lock (&srsvp->flows->mutex_lock); HASH_Iterate (srsvp->flows->table, flow) { if (count == 1) { uii_add_bulk_output (uii, "%2s %4s %5s %15s %5s %5s %15s %5s %15s %3s " "%1s %4s %4s %3s %2s %2s %2s %2s\n", "NO", "S/R", "STAT", "DESTINATION", "PROTO", "DPORT", "SENDER", "SPORT", "UPSTREAM", "IF", "P", "MTU", "PPS", "SEC", "CD", "CF", "RD", "RF"); } uii_add_bulk_output (uii, "%2d", count++); uii_add_bulk_output (uii, " %4s", BIT_TEST (flow->flags, SRSVP_FLOWF_SENDER)? "SEND": BIT_TEST (flow->flags, SRSVP_FLOWF_RECVER)? "RECV": "FWRD"); uii_add_bulk_output (uii, " %5s", BIT_TEST (flow->flags, SRSVP_FLOWF_DELETED)? "DELED": BIT_TEST (flow->flags, SRSVP_FLOWF_READY)? "READY": BIT_TEST (flow->flags, SRSVP_FLOWF_RESV1)? "RESV1": BIT_TEST (flow->flags, SRSVP_FLOWF_RESV1)? "RESV0": " "); uii_add_bulk_output (uii, " %15a %5d %5d %15a %5d", flow->destin, flow->proto, flow->dport, flow->sender, flow->sport); if (flow->upstream) { uii_add_bulk_output (uii, " %15a %3s", flow->upstream->prefix, flow->upstream->vif->interface->name); } else { uii_add_bulk_output (uii, " %15s %3s", "", ""); } if (flow->req_qos) { uii_add_bulk_output (uii, " %1u %4u %4u %3u %2u %2u %2u %2u", flow->req_qos->pri, flow->req_qos->mtu, flow->req_qos->pps, flow->req_qos->sec, flow->req_qos->cd, flow->req_qos->cf, flow->req_qos->rdly, flow->req_qos->rfee); } if (flow->errcode) { char text[256]; srsvp_error_spec_string (flow->errcode, text); uii_add_bulk_output (uii, " %s", text); } uii_add_bulk_output (uii, "\n"); if (flow->ll_downstreams) { srsvp_leaf_t *leaf; LL_Iterate (flow->ll_downstreams, leaf) { uii_add_bulk_output (uii, "%2s", ""); uii_add_bulk_output (uii, " %4s", "LEAF"); uii_add_bulk_output (uii, " %5s", BIT_TEST (flow->flags, SRSVP_FLOWF_DELETED)? "DELED": BIT_TEST (leaf->flags, SRSVP_LEAFF_READY)? "READY": BIT_TEST (leaf->flags, SRSVP_LEAFF_RESV1)? "RESV1": BIT_TEST (leaf->flags, SRSVP_LEAFF_RESV0)? "RESV0": " "); uii_add_bulk_output (uii, " %15a %49s %3s", leaf->neighbor->prefix, "", leaf->neighbor->vif->interface->name); if (leaf->req_qos) { uii_add_bulk_output (uii, " %1u %4u %4u %3u %2u %2u %2u %2u", leaf->req_qos->pri, leaf->req_qos->mtu, leaf->req_qos->pps, leaf->req_qos->sec, leaf->req_qos->cd, leaf->req_qos->cf, leaf->req_qos->rdly, leaf->req_qos->rfee); } uii_add_bulk_output (uii, "\n"); } } } pthread_mutex_unlock (&srsvp->flows->mutex_lock); return (1); } .