/* * * @(#) $Id: rsvp_diag.c,v 4.14 1998/07/23 17:08:43 kann Exp $ */ /************************ rsvp_diag.c ******************************* * * * Routines to receive, process, and send diagnostic * * messages. * * * * * *********************************************************************/ /**************************************************************************** RSVPD -- ReSerVation Protocol Daemon USC Information Sciences Institute Marina del Rey, California Original Version: Shai Herzog, Nov. 1993. Current Version: Steven Berson & Bob Braden, May 1996. 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" #include /* external declarations */ int rsvp_pkt_process(struct packet *, net_addr *, int); Object_header *copy_object(Object_header *); int send_pkt_out_if(int, RSVP_HOP *, struct packet *); struct packet *new_packet_area(packet_area *); PSB *locate_PSB(Session *, SENDER_TEMPLATE *, int, RSVP_HOP *); int send_msgto(int, struct sockaddr_in *, struct in_addr *,struct packet *); int match_filt2star(FILTER_SPEC *, FiltSpecStar *); void FQkill(Fobject **); /* forward declarations */ RSB *locate_RSB_diag(Session *, RSVP_HOP *, FILTER_SPEC *, int); int accept_diag_request(int, struct packet *, net_addr *); int accept_diag_reply(int, struct packet *); int send_diag_reply(struct packet *, int, u_char, RSVP_HOP *,int, DIAG_RESPONSE *); extern int bmptoif(bitmap *); extern int rsrr_route_query(void*,void *,net_addr *,net_addr *,int,int *,bitmap *); #define JAN_1970 2208988800UL /*1970-1900 in seconds */ /* definitions for margin in rsvp diag replies and other constants */ #define ROUTE_MARGIN (pkt->pkt_map->rsvp_resplist-1)*sizeof(struct in_addr)+ \ sizeof(ROUTE) #define packet_MTU pkt_map->rsvp_diag->diag_pMTU /* * accept_diag_request() : Process an incoming DREQ (diagnostics request) * (DREQ forwarding) * The local diagnostic response and optional ROUTE object are * inserted. */ int accept_diag_request(int in_vif, struct packet *pkt, net_addr *fromp) { struct timeval tp; Session *destp; DIAG_RESPONSE *d_resp; u_char hop_count; u_char flags = 0, H; #define DREQ_TRIMFWD 0x01 #define DREQ_MAXHOP 0x02 if_rec phys_int, phys_fw_int, in_int; RSB *rsbp = NULL, tRSB; PSB *psbp = NULL; FILTER_SPEC *sendr_filtp; RSVP_HOP err_hop, nhop, *fwhop = NULL; ROUTE *droute, *dr_old = NULL; int mode = 0; int num_of_resp, fw_if, rc; net_addr laddr, saddr; Object_header *objp,*old_objp; /* Diagnostics processing at this hop : * If this packet already has embedded replies, and this node is the * LAST HOP, there could be a loop. In this case we will just drop * the packet. The other three combinations of (embedded-replies?, * last-hop?) are valid. */ NET_SET_ADDR_IPv4(&laddr,pkt->rsvp_diagnostic->diag_laddr); NET_SET_ADDR_IPv4(&saddr,pkt->rsvp_diagnostic->diag_saddr); if ( (pkt->rsvp_drespnum != 0) && (map_if_addr(&laddr) >=0)) return -1; H = DIAG_HBIT(pkt->rsvp_diagnostic); gettimeofday(&tp, 0); num_of_resp = pkt->pkt_map->rsvp_resplist; /* Increment the RSVP-hop-count field in the diagnostic packet * header */ pkt->rsvp_diagnostic->diag_hopcount++; /* If this packet already contained a ROUTE object, copy it out * for mapping it back in later after expanding it. */ if (H == 1) dr_old = (ROUTE *)copy_object( (Object_header *) pkt->pkt_map->rsvp_route); /* * Initialize the DIAG_RESPONSE et al */ d_resp = (DIAG_RESPONSE *)((char *)pkt->pkt_data + pkt->pkt_len); Init_Object(d_resp, DIAG_RESPONSE, DIAG_RESPONSE_ipv4); /* Init_Object(&sendr_filtp, FILTER_SPEC, ctype_FILTER_SPEC_ipv4);*/ Init_Object(&err_hop, RSVP_HOP, RSVP_HOP_ipv4); /* Set up the nhop, for locating RSB from RSB list later */ if (fromp) { Init_Object(&nhop, RSVP_HOP, RSVP_HOP_ipv4); nhop.hop4_addr = NET_GET_ADDR_IPv4(fromp); } /* Set DREQ arrival timer - 32bit NTP timestamp is use */ d_resp->resp_arrtime = ((tp.tv_sec+JAN_1970) << 16) + (tp.tv_usec << 10)/15625 ; hop_count= pkt->pkt_data->rsvp_snd_TTL - pkt->pkt_ttl; d_resp->resp_DTTL = hop_count; objp = Next_Object(d_resp); /* If no PATH state exists for the specified session, set * R-error in the Response Data object */ destp = locate_session(pkt->rsvp_sess); if (!destp) d_resp->resp_Rerror |= (RSVP_Erv_Diag_NOPATH << 4); else { /* Session exists * Locate the sender's path state in the session block */ sendr_filtp = &pkt->pkt_map->rsvp_diag->diag_sfiltp; psbp = locate_PSB(destp, sendr_filtp, -1,NULL); if (!psbp) { /* No path state for this sender */ d_resp->resp_Rerror=(RSVP_Erv_Diag_NOPATH << 4); } else { /* * fwhop and fw_if are used to forward the DREQ upstream */ fwhop = &psbp->ps_rsvp_phop; phys_int=if_vec[IF_UNICAST(in_vif)]; fw_if = psbp->ps_in_if; phys_fw_int=if_vec[IF_UNICAST(fw_if)]; /* If path MTU value is too large, set "MTU too large" * error bit, and change MTU value to that of the current * router. * * RSVP_DIAGS! : DISABLE this check now because if_vec's mtu is 0! * As a result, the MTU too large error will never happen. * But the trim and forward function will still work. if (pkt->packet_MTU > phys_fw_int.if_path_mtu) { pkt->packet_MTU = phys_fw_int.if_path_mtu; d_resp->response_Rerror |= (RSVP_Erv_Diag_MTUBIG << 4); } */ d_resp->resp_timeval = (u_int16_t) ((destp->d_refr_p.d_timeval.timev_R) /1000); in_int = if_vec[IF_UNICAST((int) psbp->ps_in_if)]; d_resp->resp_in_addr = NET_GET_ADDR_IPv4(&NET_GET_IF_PHY_ADDR(&in_int.if_addr)); d_resp->resp_out_addr = NET_GET_ADDR_IPv4(&NET_GET_IF_PHY_ADDR(&phys_int.if_addr)); d_resp->resp_pre_addr = psbp->ps_phop.hop4_addr ; /* * Append RSVP objects containing variable response * information from the RSB and LLSB onto the end of * the outer DIAG_RESPONSE object. * Warning : Buffer overflow is not being checked for currently. */ /* Append Tspec of sender; save object location for overwriting * with effect traffic control tspec later, if reservation exists */ old_objp = objp; Move_Object(psbp->ps_tspec, objp); objp = Next_Object(objp); /* Find a reservation if it exists and append traffic control state */ rsbp = locate_RSB_diag(destp, &nhop,sendr_filtp, in_vif); if (rsbp != NULL) { /* Found RSB which matches outgoing Iface */ d_resp->resp_rstyle = rsbp->rs_style; /* K value */ d_resp->resp_Rerror |= K_FACTOR; /* * Build temporary RSB as parm list and * Extract effective link-layer values. */ tRSB.rs_OIf = rsbp->rs_OIf; tRSB.rs_filtstar = rsbp->rs_filtstar; tRSB.rs_style = rsbp->rs_style; tRSB.rs_UnkObjList = NULL ; rc = LL_GetInfo(tRSB.rs_OIf, destp, &tRSB); if (rc == LLDAL_RC_OK) { int i; if (tRSB.rs_flags & RSB_FLAG_MERGED) d_resp->resp_Rerror |=( 0x01 << 7 ); /* * Override Tspec with effective Tspec from LL; */ objp = old_objp; Move_Object(tRSB.rs_oldspec,objp); objp = Next_Object(objp); /* Append Effective flowspec */ Move_Object(tRSB.rs_spec, objp); objp = Next_Object(objp); /* Append all filter specs (senders) using * this (LL) reservation. */ for (i=0; ipkt_map->rsvp_diag_response) pkt->pkt_map->rsvp_diag_response = d_resp ; pkt->pkt_map->rsvp_resplist ++; /* Update current pkt_len */ pkt->pkt_len += Obj_Length(d_resp); /* If we've reached the max hop limit, set a flag. */ if (pkt->rsvp_diagnostic->diag_hopcount == pkt->rsvp_diagnostic->diag_maxhops) flags |= DREQ_MAXHOP; /* if the resulting DREQ packet size exceeds MTU limit, and * if we have not reach the max hop limit, and this hop is not * the diagnosed sender, set the TRIMFWD flag. */ if (pkt->pkt_len > pkt->packet_MTU && !(flags & DREQ_MAXHOP) && (map_if_addr(&saddr) < 0)) flags |= DREQ_TRIMFWD ; /* For HBH mode of reply, if this is the last hop, create and * initialize a ROUTE object. */ if (H == 1) { droute = (ROUTE *)((char *)pkt->pkt_data + pkt->pkt_len); if (map_if_addr(&laddr) >=0) { /* Last hop... */ Init_Object(droute, ROUTE, ROUTE_ipv4); Obj_Length(droute) -= sizeof(d_resp->resp_in_addr); droute->R_pointer_ipv4 = -1; pkt->pkt_len += Obj_Length(droute); } else { /* Not last hop; copy save route obj to resp tail. */ assert(dr_old != NULL); droute = (ROUTE *)((char *)pkt->pkt_data + pkt->pkt_len); Move_Object(dr_old, droute); free(dr_old); } /* update the route object */ droute->R_pointer_ipv4++; route(droute,droute->R_pointer_ipv4) = d_resp->resp_in_addr; Obj_Length(droute) += sizeof(d_resp->resp_in_addr); /* Add the (new) route object (back) into the map */ pkt->pkt_len += sizeof(d_resp->resp_in_addr); pkt->pkt_map->rsvp_route = droute ; /* Set the ROUTEBIG error if the pkt_len has exceed ed * packet_MTU, but only after updating the route object. */ if ( !(flags & DREQ_TRIMFWD) && pkt->pkt_len > pkt->packet_MTU) d_resp->resp_Rerror |= (RSVP_Erv_Diag_ROUTEBIG << 4); } /* if no error bit is set, no flags are set, * forward the DREQ packet , else goto send_diag_reply */ if((DIAG_RESPONSE_RERROR(d_resp) == RSVP_Err_NONE) && !(flags & (DREQ_TRIMFWD | DREQ_MAXHOP)) && (map_if_addr(&saddr) < 0)) { mode = send_pkt_out_if(IF_UNICAST(fw_if),fwhop, pkt); if (mode< 0) return -1; return 0; } return(send_diag_reply(pkt,in_vif,flags,fwhop,fw_if,d_resp)); } /* * accept_diag_reply() : Forwarding incoming diagnostic response messages * (DREP Forwarding) */ int accept_diag_reply(int in_vif, struct packet *pkt) { DIAGNOSTIC *diagnostic; u_char H ; int mode; RSVP_HOP hopp; net_addr laddr; net_addr toaddr; Init_Object(&hopp, RSVP_HOP, RSVP_HOP_ipv4); diagnostic = pkt->rsvp_diagnostic; H = DIAG_HBIT(diagnostic); NET_SET_ADDR_IPv4(&laddr,diagnostic->diag_laddr); NET_SET_ADDR3_UDP_IPv4(&toaddr,diagnostic->diag_raddr,diagnostic->diag_rport); /* Are we the LAST_HOP ? */ if (map_if_addr(&laddr) >=0) { if (IN_MULTICAST(ntoh32(diagnostic->diag_raddr.s_addr))) pkt->pkt_ttl = diagnostic->diag_ttl ; mode = send_pkt_to(-1, &toaddr, pkt); } else if(H == 1) { /* We are in the middle of a HBH DREP */ pkt->rsvp_droute->R_pointer_ipv4 --; hopp.hop4_addr = route(pkt->rsvp_droute, pkt->rsvp_droute->R_pointer_ipv4); mode = send_pkt_out_if(-1, &hopp, pkt); } else /* These two cases are exceptions, not included for * in the specification */ if (IN_MULTICAST(ntoh32(diagnostic->diag_raddr.s_addr))) { hopp.hop4_addr = diagnostic->diag_laddr ; mode = send_pkt_out_if(-1, &hopp, pkt); } else { mode = send_pkt_to(-1, &toaddr, pkt); } if (mode <0) return -1; return 0; } /* * send_diag_reply : Used primarily to send diagnostic replies. * In the fragmentation case, we will trim a DREP, * build and send a DREP, and forward a *new* DREQ * to the enclosed prev hop. */ int send_diag_reply( struct packet *pkt, int in_vif, u_char flags, RSVP_HOP *fwhop, int fw_if, DIAG_RESPONSE *ldresp) { DIAG_RESPONSE *drespo, *dresp; RSVP_HOP hopp; int offset, old_off ,i ,mode; int outif; u_char H ; struct packet lpkt, *fwd_pkt = &lpkt; packet_map mapp, *fwd_map = &mapp; Session *destp; net_addr laddr; net_addr toaddr; bitmap bmp; int rv; bmp_rst(&bmp); H = DIAG_HBIT(pkt->rsvp_diagnostic); dresp = ldresp; destp = locate_session(pkt->rsvp_sess); NET_SET_ADDR_IPv4(&laddr,pkt->rsvp_diagnostic->diag_laddr); NET_SET_ADDR3_UDP_IPv4(&toaddr,pkt->rsvp_diagnostic->diag_raddr, pkt->rsvp_diagnostic->diag_rport); Init_Object(&hopp, RSVP_HOP, RSVP_HOP_ipv4); /* If there is no error and we need to trim and forward */ if ((flags & DREQ_TRIMFWD) && DIAG_RESPONSE_RERROR(dresp) == RSVP_Err_NONE) { /* XXX??? Currently the packet duplication code is a trick. * We use the current packet data buffer, and send out two packets, * with the appropriate map changes each time. For the DREQ forwarded * upstream, the packet and map data structues are created anew. * May need to revisit this section later. */ *fwd_pkt = *pkt ; *fwd_map = *pkt->pkt_map ; fwd_pkt->pkt_map = fwd_map ; assert(fwd_pkt->pkt_data->rsvp_type == RSVP_DREQ); assert(fwd_pkt->pkt_map->rsvp_msgtype == RSVP_DREQ); /* Compute the fragment offset, in terms of number of response * object bytes already, and update current offset. */ drespo = pkt->pkt_map->rsvp_diag_response; old_off = offset = pkt->rsvp_diagnostic->diag_frag_off ; for (i=0; ipkt_map->rsvp_resplist; i++) { offset += Obj_Length(drespo); (char *)drespo += Obj_Length(drespo); } fwd_pkt->pkt_map->rsvp_diag_response = NULL; fwd_pkt->pkt_map->rsvp_resplist = 0; fwd_pkt->pkt_map->rsvp_diag->diag_frag_off = offset; /* Continue and forward the trimmed DREQ packet upstream */ mode = send_pkt_out_if(IF_UNICAST(fw_if),fwhop, fwd_pkt); if (mode < 0) return -1 ; /* Set the More Fragment bit on the original packet, indicating fragmentation * since this will be the first DREP, and restore the frag_off. */ pkt->pkt_map->rsvp_diag->diag_replymode |= 0x01; pkt->pkt_map->rsvp_diag->diag_frag_off = old_off; } /* Send the DREP after turning the packet to a DREP from a DREQ. */ pkt->pkt_data->rsvp_type = RSVP_DREP; pkt->pkt_map->rsvp_msgtype = RSVP_DREP; /* Current DREP : If any error bit is set, send the packet back to either last_hop * or response address. */ if (DIAG_RESPONSE_RERROR(dresp) != RSVP_Err_NONE) { if ( map_if_addr(&laddr) < 0 ) { if (H == 1 || IN_MULTICAST(ntoh32(pkt->rsvp_diagnostic->diag_raddr.s_addr))) { hopp.hop4_addr = pkt->rsvp_diagnostic->diag_laddr; /* outif = unicast_route(hop_addr(&hopp)); */ rv = rsrr_route_query((void *)0, (void *)0, (net_addr *)0, hop_addr(&hopp), 0, (int *)0, &bmp); if (rv == -1) { log(LOG_ERR,errno,"Unicast route lookup failure"); return -1; } outif = bmptoif(&bmp); mode = send_pkt_out_if(outif, &hopp, pkt); } } else /* last hop */ mode = send_pkt_to(-1, &toaddr, pkt); if (mode < 0) return (-1); return 0; } else { /* No errors, valid DREP to send, either because * of DREQ_MAXHOP, or the DREQ_TRIMFWD case, or * XXXX we reached the sender RSVP router XXXX */ if (H == 0 || ( map_if_addr(&laddr) >= 0)) { mode = send_pkt_to(-1, &toaddr, pkt); } else { assert(pkt->rsvp_droute != NULL); pkt->rsvp_droute->R_pointer_ipv4--; hopp.hop4_addr = route(pkt->rsvp_droute, pkt->rsvp_droute->R_pointer_ipv4); /* outif = unicast_route(hop_addr(&hopp)); */ rv = rsrr_route_query((void *)0, (void *)0, (net_addr *)0, hop_addr(&hopp), 0, (int *)0, &bmp); if (rv == -1) { log(LOG_ERR,errno,"Unicast route lookup failure"); return -1; } outif = bmptoif(&bmp); mode = send_pkt_out_if(outif ,&hopp, pkt); } if (mode <0) return -1; return 0; } } /* locate_RSB_diag(): * * This routine is similar to locate_RSB, but tailor made for Diagnostic * message support. It returns an RSB if a reservation exists, for the * given session, next hop, and sender (specified by filter) * * in_vif : this is the vif the diagnostic request came in on. */ RSB * locate_RSB_diag(Session *destp, RSVP_HOP *nhopp, FILTER_SPEC *sfiltp, int in_vif) { RSB *rp = NULL ; RSB *cd_rp = NULL; /* cd_rp is used to save off a candidate RSB for the case detailed below */ for (rp = destp->d_RSB_list; rp != NULL; rp = rp->rs_next) { if (!hop_addr_eq(nhopp,&rp->rs_nhop)) { /* Kluge? for the case, where the diag request landed on a * broadcast multi-acess network (part of the reverse path). * There may be valid reservations on this router's BMA network interface, * but NHOPs may be different routers, not necessarily the router from * where the diag request got forwarded. But we still want to record the * LLB state for this OIF, so we just find some RSB for the same OIF * and call LL_GetInfo as usual. (OIF for the reservation is the incoming VIF * from the diagnostic request. * * Due to wierdness in unicast routing or the presence of tunnels, the diag request * may land up on the wrong interface. The definition of the diagnositc message header * has been changed to include for the HOP object with the LIH field, to handle this * problem. (version 3 of the specification). When this code is brought upto date, the * LIH value extracted from the HOP object (in a forwarded DIAG_REQ packet) will be used * instead of the in_vif value for correct behaviour. */ if(in_vif != rp->rs_OIf) continue; else if(!cd_rp) /* the rules to choose a candidate RSB are those that apply for * selecting a routine RSB */ if(Style_is_Shared(rp->rs_style) || match_filt2star(sfiltp, rp->rs_filtstar)) { cd_rp = rp; continue; } } if(Style_is_Shared(rp->rs_style) || match_filt2star(sfiltp, rp->rs_filtstar)) break; } if (rp) return(rp); else return(cd_rp); }