/********************************************************************* * * * IPv4/IPv6 RSRR interfaces for RSVP * * * *********************************************************************/ /**************************************************************************** RSVPD -- ReSerVation Protocol Daemon USC Information Sciences Institute Marina del Rey, California RSRR written by: Daniel Zappala, April 1995 RSRR modified by: Jeff Kann, Oct 1997 Copyright (c) 1996 by the University of Southern California All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation in source and binary forms for any purpose and without fee is hereby granted, provided that both the above copyright notice and this permission notice appear in all copies. and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed in part by the University of Southern California, Information Sciences Institute. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission. THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Other copyrights might apply to parts of this software and are so noted when applicable. ********************************************************************/ #include "rsvp_daemon.h" int lnum; int rsrr_initialized = 0; int rsrr_initial_reply; void rsrr_update(); extern int if_num; extern int rsrr_socket; #ifdef USE_IPV6 extern int rsrr_socket_v6; #endif /* USE_IPV6 */ extern int asynchronous; extern char rsrr_recv_buf[]; extern int shift; extern int rsrr_use_v2_ipv4; /* RSRR v2 being used for IPv4 */ #if defined(PF_ROUTE) && !defined(sgi_53) extern int rskt; #endif /* PF_ROUTE */ extern void route_update(Session *, PSB *, int , bitmap); extern void finish_path(Session *, PSB *); extern int unicast_route(net_addr *addr); extern net_addr * net_addr_ip(net_addr *); extern int if_list_init(); extern int rsrr_read(); extern int rsrr_init(); extern int rsrr_send_rq(); extern struct rsrr_query_table *rsrr_qt_lookup(); extern void rsrr_qt_delete(); extern const char *net_addr_print(const net_addr *); extern char *bm_expand(); /* Convert a bitmap to one interface number for unicast routing case */ int bmptoif(bitmap *); #ifdef USE_IPV6 char * ip6_sprintf(); #endif /* USE_IPV6 */ #ifndef HOST_ONLY /* No RSRR code for HOST_ONLY */ extern int rsrr_send_iq(); /* Forward declaration of the interfaces of RSRR */ /* * For dispatching and handling RSRR incoming messages */ int rsrr_dispatch(); /* * To fill out the vif_list originally during rsrr_init */ int rsrr_interface_query(int); /* * The corresponding upcall for rsrr_interface_query() during init * or notification of interface changes */ int rsrr_interface_reply(void); /* * To find out the route for unicast and multicast */ int rsrr_route_query(void*,void *,net_addr *,net_addr *,int,int *,bitmap *); /* * The corresponding upcall for rsrr_route_query() after a route_query * or for notification of route changes */ int rsrr_route_reply(u_long, net_addr *, net_addr *, int, bitmap *, int, int, int); /* * For doing dispatch of the incoming messages received * or performing timeout when no mrouted present */ int rsrr_dispatch() { int rc = 0; fd_set tmp_fds; int n, fd_wid; struct timeval tout; int max; int needed = 0; int nomore = 0; int received_v4 = 0; #ifdef USE_IPV6 int received_v6 = 0; #endif /* USE_IPV6 */ FD_ZERO(&tmp_fds); memset(&tout, 0, sizeof(tout)); fd_wid = -1; if (rsrr_initial_reply) { /* * this function is called to wait for the interface * reply for the first time */ /* reset this value */ rsrr_initial_reply = 0; if ((NoV4Mroute) #ifdef USE_IPV6 && (NoV6Mroute) #endif /* USE_IPV6 */ ){ /* * we have decided earlier that there is no * multicast daemons running. perform necessary * task such as add_interface in rsrr_interface_reply * then return to caller. */ rc = rsrr_interface_reply(); return rc; } if (!NoV4Mroute) { /* * we already knew IPv4 Mroute daemon is running * so we should listen to rsrr_socket */ FD_SET(rsrr_socket, &tmp_fds); ++needed; max = rsrr_socket; } #ifdef USE_IPV6 if (!NoV6Mroute) { /* * we already knew IPv6 Mroute daemon is running * so we should listen to rsrr_socket_v6 */ FD_SET(rsrr_socket_v6, &tmp_fds); ++needed; max = (rsrr_socket > rsrr_socket_v6) ? rsrr_socket : rsrr_socket_v6; } #endif /* USE_IPV6 */ fd_wid = max + 1; /* * timeout in 5 seconds if no response from rsrr_socket(_v6) */ tout.tv_sec = 5; tout.tv_usec = 0; for (;;) { /* * if we don't need anything else, just return */ if (nomore == 1) break; n = select(fd_wid, &tmp_fds, NULL, NULL, &tout); /* * we have got all we wanted, no more */ if (n == needed) nomore = 1; if (n < 0) { /* * select error */ if (!NoV4Mroute && !received_v4) /* * we thought IPv4 mroute daemon was * running, but we haven't received * it yet */ NoV4Mroute = 1; #ifdef USE_IPV6 if (!NoV6Mroute && !received_v6) /* * we thought IPv6 mroute daemon was * running, but we haven't received * it yet */ NoV6Mroute = 1; #endif /* USE_IPV6 */ log(LOG_ERR,errno,"select() error",0); exit(-1); } else if (n == 0) { /* * timeout */ if (!NoV4Mroute && !received_v4) /* * we thought IPv4 mroute daemon was * running, but we haven't received * it yet */ NoV4Mroute = 1; #ifdef USE_IPV6 if (!NoV6Mroute && !received_v6) /* * we thought IPv6 mroute daemon was * running, but we haven't received * it yet */ NoV6Mroute = 1; #endif /* USE_IPV6 */ rc = rsrr_interface_reply(); return rc; } else { if (FD_ISSET(rsrr_socket, &tmp_fds) && !NoV4Mroute) { /* * got something on rsrr_socket, we do * have V4 mroute daemon running */ --needed; received_v4 = 1; NoV4Mroute = 0; /* * read the socket and deal with the * packet */ rc = rsrr_read(RSRR_SOCK_V4UX,RSRR_INTERFACE_REPLY); if (rc < 0) NoV4Mroute = 1; #ifdef USE_IPV6 if (!NoV6Mroute && needed > 0) FD_SET(rsrr_socket_v6, &tmp_fds); #endif /* USE_IPV6 */ } #ifdef USE_IPV6 if (FD_ISSET(rsrr_socket_v6, &tmp_fds) && !NoV6Mroute) { /* * got something on rsrr_socket_v6, we * do have v6 mroute daemon running */ --needed; received_v6 = 1; NoV6Mroute = 0; /* * read the socket and deal with the * packet */ rc = rsrr_read(RSRR_SOCK_V6UX,RSRR_INTERFACE_REPLY); if (rc < 0) NoV6Mroute = 1; if (!NoV4Mroute && needed > 0) FD_SET(rsrr_socket, &tmp_fds); } #endif /* USE_IPV6 */ } } return rsrr_interface_reply(); } #ifndef HOST_ONLY if (!NoV4Mroute) { /* * listen to the socket used to talk to v4 mroute daemon */ FD_SET(rsrr_socket, &tmp_fds); if (fd_wid < rsrr_socket) fd_wid = rsrr_socket; } #ifdef USE_IPV6 if (!NoV6Mroute) { /* * listen to the socket used to talk to v4 mroute daemon */ FD_SET(rsrr_socket_v6, &tmp_fds); if (fd_wid < rsrr_socket_v6) fd_wid = rsrr_socket_v6; } #endif /* USE_IPV6 */ #if defined(PF_ROUTE) && !defined(sgi_53) if (!NoUnicast) { /* * listen to the routing socket for unicast routes */ FD_SET(rskt, &tmp_fds); if (fd_wid < rskt) fd_wid = rskt; } #endif /* PF_ROUTE */ #endif /* ! HOST_ONLY */ fd_wid += 1; /* * Polling for input ready on sockets */ n = select(fd_wid, &tmp_fds, NULL, NULL, &tout); if (FD_ISSET(rsrr_socket, &tmp_fds) && !NoV4Mroute) { rc = rsrr_read(RSRR_SOCK_V4UX,RSRR_ALL_TYPES); } #ifdef USE_IPV6 else if (FD_ISSET(rsrr_socket_v6, &tmp_fds) && !NoV6Mroute) { rc = rsrr_read(RSRR_SOCK_V6UX,RSRR_ALL_TYPES); } #endif /* USE_IPV6 */ #if defined(PF_ROUTE) && !defined(sgi_53) else if (FD_ISSET(rskt, &tmp_fds) && !NoUnicast) { rc = rsrr_read(RSRR_SOCK_RT,RSRR_ALL_TYPES); } #endif /* PF_ROUTE */ return rc; } /* * RSRR sends out interface query to routing daemons and kernel, then * stores the returned info in global variable vif_list * - Argument: note - notification flag * - Returns: success or fail */ int rsrr_interface_query(int note) { int rc; /* * This function should only be called once */ if (!rsrr_initialized) { /* * Build if_vec */ if_num = if_list_init(); lnum = if_num; /* * Initialize RSRR sockets */ rsrr_init(); rsrr_initialized = 1; } else { log(LOG_ERR,errno,"rsrr_interface_query() called twice",0); return -1; } /* * IPv4/IPv6 muliticast routing daemon */ if ((!NoV4Mroute) #ifdef USE_IPV6 || (!NoV6Mroute) #endif /* USE_IPV6 */ ){ /* * only send query when mroute daemons are present */ rc = rsrr_send_iq(note); return rc; } else return 0; } /* * The corresponding upcall for rsrr_interface_query() during init * or notification of interface changes * * Argument: num - number of vif present, 0 for NoV4Mroute * list - returned vif_list * * Returns: 0 if success, * -1 if failed. */ int rsrr_interface_reply() { net_if interfaces[FD_SETSIZE]; int i,ninterfaces = 0; /* * the whole function needs to be redesigned once interface * change notification is needed here */ /* * For now, just assume it's only called once when a reply is back * from mrouted or NoV4Mroute is true */ for (i = 0;i < if_num; i++) { if (IsNumAPI(i)) /* * skip the API interface */ continue; if (IF_DOWN(i)) /* * skip the interface which is marked down */ continue; /* * otherwise, add this interface to the table */ interfaces[ninterfaces++] = if_vec[i].if_addr; } if (FAILED(net_add_interfaces(ninterfaces,interfaces))) { log(LOG_ERR,errno,"net_add_interfaces"); return(SYS_ERROR); } return 0; } /* * RSRR sends route query to a particular routing daemons based on * the address type, or query the kernel directly. * * Argument: psb - pointer to PSB block in multicast case * ses - pointer to Session in multicast case * src - source address, * dst - destination address, * note - notification flag, * iif - incoming interface * bmp - outgoing interface bitmap * * Returns: -1 if error occurs * 0 if reply is synchronous, and it's contained in bmp * >0 if reply is asynchronous, return sequence num */ int rsrr_route_query(void *psb , void *ses, net_addr *src, net_addr *dst, int note, int *iif, bitmap *bmp) { int rv = 0; if (!dst) { log(LOG_ERR,errno,"Null address for query",0); return (-1); } /* * IPv4 */ if ((net_addr_ip(dst))->type == NET_ADDR_IPv4) { /* * IPv4 Unicast */ if (!IN_IS_ADDR_MULTICAST(&(NET_GET_ADDR_IPv4(net_addr_ip(dst))))) { if (NoUnicast) return -1; if (src == 0) { /* correct, go on */ rv = unicast_route(dst); if (rv == -1) { /* * Error occurred */ return (-1); } #if defined(PF_ROUTE) && !defined(sgi_53) else if (rv == 0 && asynchronous == 1) { /* * Asynchronous reply, * return sequence number */ return seq; } #endif /* PF_ROUTE */ else { /* * Synchronous reply, * set up bitmap then return */ bmp_set(bmp, rv); asynchronous = 1; return (0); } } else { log(LOG_ERR,errno,"Invalid IPv4 unicast address for query",0); return (-1); } } /* * IPv4 Multicast */ else { if (NoV4Mroute) return -1; /* * Make sure src is not null, and it's not multicast * address */ if (src != 0 && !IN_IS_ADDR_MULTICAST(&(NET_GET_ADDR_IPv4(net_addr_ip(src))))) { #ifdef HOST_ONLY assert(0); #else /* !HOST_ONLY */ /* * send out the route query if this route query is not * intended to delete the route change notification * cache in the routing daemon or if it is, make sure * RSRRv2 is the one used by IPv4 multicast routing * daemon, since the current implementation of RSRR * stub in mrouted uses RSRRv1 and it has no processing * for route change notification cache delete request * from rsvpd. * rv is either -1 for error or query_id */ if (note || rsrr_use_v2_ipv4) rv = rsrr_send_rq(psb, ses, AF_INET, src, dst, note); return rv; #endif /* !HOST_ONLY */ } else { log(LOG_ERR,errno,"Invalid IPv4 multicast address for query",0); return (-1); } } } #ifdef USE_IPV6 else if ((net_addr_ip(dst))->type == NET_ADDR_IPv6) { /* * IPv6 Unicast */ if (!IS_V6MULTI(NET_GET_ADDR_IPv6(net_addr_ip(dst)))) { if (NoUnicast) return -1; if (src == 0) { /* correct, go on */ rv = unicast_route(dst); if (rv == -1) { /* * Error occurred */ return (-1); } #if defined(PF_ROUTE) && !defined(sgi_53) else if (rv == 0 && asynchronous == 1) { /* * Asynchronous reply */ return seq; } #endif /* PF_ROUTE */ else { /* * Synchronous reply */ bmp_set(bmp, rv); asynchronous = 1; return (0); } } else { log(LOG_ERR,errno,"Incorrect address for query",0); return (-1); } } /* * IPv6 Multicast */ else { if (NoV6Mroute) return -1; /* * Make sure src is not null, and it's not multicast * address */ if (src != 0 && !IS_V6MULTI(NET_GET_ADDR_IPv6(net_addr_ip(src)))) { if (NoV6Mroute) return -1; #ifdef HOST_ONLY assert(0); #else /* !HOST_ONLY */ /* * send out route query * rv is either -1 for error or query_id */ rv = rsrr_send_rq(psb, ses, AF_INET6, src, dst, note); return rv; #endif /* !HOST_ONLY */ } } } #endif /* USE_IPV6 */ else { log(LOG_ERR,errno,"No such type exists",0); return (-1); } /* * if here, must be an error */ return -1; } #endif /* HOST_ONLY */ /* * The corresponding upcall for rsrr_route_query() after a route_query * or for notification of route changes * * Argument: qid - query ID. Could be PSB address in multicast case, * or rtm_seq in routing socket case. * src - source address * dst - destination address * iif - incoming interface * bmp - outgoing interface bitmap * shared, all_shared - for shared tree operation * * Returns: 0 if succeed. */ int rsrr_route_reply(u_long qid, net_addr *src, net_addr *dst, int iif, bitmap *bmp, int note, int shared, int all_shared) { struct rsrr_query_table *qt; Session *dp; PSB *sp; int i; /* * Unicast case */ if (src == (net_addr *)0 && dst != (net_addr *)0) { /* * Unicast routing reply, result is stored in bmp, * which should be the global variable bmp_unicast. */ if (unicast_query_waiting == 1) { /* * Asynchronous route reply */ unicast_result_ready = 1; return 0; } else { /* * route change notification */ for (i= 0; i < SESS_HASH_SIZE; i++) { for (dp = session_hash[i]; dp != NULL; dp = dp->d_next) { if (NET_GET_TYPE(net_addr_ip(dst)) == NET_ADDR_IPv4) { /* * IPv4 unicast case */ if (dp->d_session->sess4_addr.s_addr == NET_GET_ADDR_IPv4(net_addr_ip(dst)).s_addr) { /* * Found session with same group/dest address. */ for (sp=dp->d_PSB_list; sp != NULL; sp=sp->ps_next) { SetNotifyBit(sp, note); rsrr_update(dp, sp, iif, bmp); } } } #ifdef USE_IPV6 else if (NET_GET_TYPE(net_addr_ip(dst)) == NET_ADDR_IPv6) { /* * IPv6 unicast case */ if (SAME_IN6ADDR(dp->d_session->sess6_addr, NET_GET_ADDR_IPv6(net_addr_ip(dst)))) { /* * Found session with same group/dest address. */ for (sp=dp->d_PSB_list; sp != NULL; sp=sp->ps_next) { SetNotifyBit(sp, note); rsrr_update(dp, sp, iif, bmp); } } } #endif /* USE_IPV6 */ } } } return 0; } /* * Multicast case */ /* * Check if query id is cached. */ qt = rsrr_qt_lookup(qid); if (qt != NULL) { SetNotifyBit((PSB *)(qt->ptrP), note); /* * Update PSB with new route info, then complete process of * path info with any needed refreshes. */ rsrr_update((Session *)(qt->ptrS), (PSB *)(qt->ptrP), iif, bmp); /* * Delete the query entry from the table. We only use the * table for pending queries. Route change notification * queries can suffer a longer lookup because they are rare. */ rsrr_qt_delete(qt->query_id); return 0; } /* * None cached. Must scan entire state list looking for * affected sessions and senders. */ for (i= 0; i < SESS_HASH_SIZE; i++) { for (dp = session_hash[i]; dp != NULL; dp = dp->d_next) { if (NET_GET_TYPE(net_addr_ip(dst)) == NET_ADDR_IPv4) { /* * IPv4 multicast case */ if (dp->d_session->sess4_addr.s_addr == NET_GET_ADDR_IPv4(net_addr_ip(dst)).s_addr) { /* * Found session with same group/dest address. */ /* * Find matching senders. If sender in RSRR is zero, * the tree is a shared tree, so match all senders. */ for (sp = dp->d_PSB_list; sp != NULL; sp = sp->ps_next) { if ((NET_GET_ADDR_IPv4(net_addr_ip(src)).s_addr == 0) || (sp->ps_templ->filt4_srcaddr.s_addr == NET_GET_ADDR_IPv4(net_addr_ip(src)).s_addr)) { SetNotifyBit(sp, note); rsrr_update(dp, sp, iif, bmp); } } } } #ifdef USE_IPV6 else if (NET_GET_TYPE(net_addr_ip(dst)) == NET_ADDR_IPv6) { /* * IPv6 multicast case */ if (SAME_IN6ADDR(dp->d_session->sess6_addr, NET_GET_ADDR_IPv6(net_addr_ip(dst)))) { /* * Found session with same group/dest address. */ /* * Find matching senders. If sender in RSRR is zero, * the tree is a shared tree, so match all senders. */ for (sp = dp->d_PSB_list; sp != NULL; sp = sp->ps_next) { if (SAME_IN6ADDR(NET_GET_ADDR_IPv6(net_addr_ip(src)), in6addr_any) || (SAME_IN6ADDR(sp->ps_templ->filt6_srcaddr, NET_GET_ADDR_IPv6(net_addr_ip(src))))) { SetNotifyBit(sp, note); rsrr_update(dp, sp, iif, bmp); } } } } #endif /* USE_IPV6 */ } } return 0; } /* * Update path state using route reply */ void rsrr_update(dp, sp, inif, bmp) Session *dp; PSB *sp; int inif; bitmap *bmp; { net_addr *addr = net_addr_ip(session_addr(dp->d_session)); /* * Remember that the multicast forwarding entry doesn't include * the vif over which data originates if the sender is local to * the router. Set the bit now. */ if (session_multicast(dp->d_session)) { if (IsHopAPI(&sp->ps_phop)) { if ((addr->type == NET_ADDR_IPv4) && !NoV4Mroute) bmp_set(bmp, sp->ps_originvif + shift); else if ((addr->type == NET_ADDR_IPv4) && NoV4Mroute) bmp_set(bmp, sp->ps_originvif); #ifdef USE_IPV6 else if (addr->type == NET_ADDR_IPv6) bmp_set(bmp, sp->ps_originvif); #endif /* USE_IPV6 */ } } /* * Call common routine to update the state. */ route_update(dp, sp, inif, *bmp); finish_path(dp, sp); } /* * bmptoif() searches through the bitmap, finds the bit which is set, * confirm it is the only bit which is set, then return that number. * - Argument: bitmap with one-bit set (bitmap*) * - Returns: interface number (int) */ int bmptoif(bitmap *bmp) { int i; char s[100]; bitmap y; /* * No bit is set */ if (bmp_zero(bmp)) return -1; /* * Search from the right-most bit of the bitmap */ for (i=0; i < BMP_SIZE*NBBY; i++) { if (bmp_tst(bmp, i)) { y = *bmp; bmp_clr(&y, i); /* * make sure only 1 bit is set */ if (bmp_zero(&y)) { return i; } else { log(LOG_DEBUG,0,"RSRR: bmp = %d",bm_expand(bmp,s)); return -1; } } } return -1; } /* * Convert IPv6 address to printable (loggable) representation. */ #ifdef USE_IPV6 static char digits[] = "0123456789abcdef"; static int ipv6round = 0; char * ip6_sprintf(addr) register struct in6_addr *addr; { static char ipv6buf[8][46]; register int i; register char *cp; register u_int16_t *a = (u_int16_t *)addr; register u_int8_t *d; int dcolon = 0; ipv6round = (ipv6round + 1) & 7; cp = ipv6buf[ipv6round]; for (i = 0; i < 8; i++) { if (dcolon == 1) { if (*a == 0) { if (i == 7) *cp++ = ':'; a++; continue; } else dcolon = 2; } if (*a == 0) { if (dcolon == 0 && i == 7) { *cp++ = '0'; *cp++ = '0'; break; } if (dcolon == 0 && *(a + 1) == 0) { if (i == 0) *cp++ = ':'; *cp++ = ':'; dcolon = 1; } else { *cp++ = '0'; *cp++ = ':'; } a++; continue; } d = (u_int8_t *)a; *cp++ = digits[*d >> 4]; *cp++ = digits[*d++ & 0xf]; *cp++ = digits[*d >> 4]; *cp++ = digits[*d & 0xf]; *cp++ = ':'; a++; } *--cp = 0; return (ipv6buf[ipv6round]); } #endif /* USE_IPV6 */