/* * @(#) $Id: rsvp_util2.c,v 4.11 1998/02/18 00:48:23 lindell Exp $ */ /************************ rsvp_util.c ******************************* * * * Common routines for managing state and parsing protocol * * data structure (flowspecs, filterspecs, flow descriptors...) * * Used by rsvp_path.c and rsvp_resv.c * * * *********************************************************************/ /**************************************************************************** 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" /* External declarations */ extern void del_from_timer(); extern int IsRoutePSB2nhop(Session *, PSB *, RSVP_HOP *); extern Object_header * copy_object(Object_header *); /* Forward declarations */ int match_filter(FILTER_SPEC *, FILTER_SPEC *); int match_policy(POLICY_DATA *, POLICY_DATA *); void scope_catf(SCOPE **, FILTER_SPEC *); int form_scope_union(Session *); void clear_scope_union(Session *); Session *locate_session(SESSION *); Session *locate_session_p(SESSION *); /* * match_filter(): Compares two FILTER_SPEC objects for equality, * returns Boolean value. */ int match_filter(FILTER_SPEC *f1, FILTER_SPEC *f2) { if (!f1 && !f2 ) return(1); if (!f1 || !f2) return(0); if (Obj_CType(f2) != Obj_CType(f1)) return(0); switch (Obj_CType(f1)) { case ctype_FILTER_SPEC_ipv4: { FILTER_SPEC_ipv4 *f1a = &f1->filt4; FILTER_SPEC_ipv4 *f2a = &f2->filt4; return (IN_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&inaddr_any) || IN_ARE_ADDR_EQUAL(&f2a->filt_ipaddr,&inaddr_any) || (IN_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&f2a->filt_ipaddr) && f1a->filt_port == f2a->filt_port) ); } case ctype_FILTER_SPEC_ipv4GPI: { FILTER_SPEC_ipv4GPI *f1a = &f1->filtgpi4; FILTER_SPEC_ipv4GPI *f2a = &f2->filtgpi4; return (IN_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&inaddr_any) || IN_ARE_ADDR_EQUAL(&f2a->filt_ipaddr,&inaddr_any) || (IN_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&f2a->filt_ipaddr) && f1a->filt_gpi == f2a->filt_gpi) ); } #ifdef USE_IPV6 case ctype_FILTER_SPEC_ipv6: { FILTER_SPEC_ipv6 *f1a = &f1->filt6; FILTER_SPEC_ipv6 *f2a = &f2->filt6; return (IN6_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&in6addr_any) || IN6_ARE_ADDR_EQUAL(&f2a->filt_ipaddr,&in6addr_any) || (IN6_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&f2a->filt_ipaddr) && f1a->filt_port == f2a->filt_port) ); } case ctype_FILTER_SPEC_ipv6GPI: { FILTER_SPEC_ipv6GPI *f1a = &f1->filtgpi6; FILTER_SPEC_ipv6GPI *f2a = &f2->filtgpi6; return (IN6_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&in6addr_any) || IN6_ARE_ADDR_EQUAL(&f2a->filt_ipaddr,&in6addr_any) || (IN6_ARE_ADDR_EQUAL(&f1a->filt_ipaddr,&f2a->filt_ipaddr) && f1a->filt_gpi == f2a->filt_gpi) ); } #endif /* USE_IPV6 */ case ctype_FILTER_SPEC_lsp_tunv4: { FILTER_SPEC_lsp_tunv4 *f1a = &f1->filtlsp4; FILTER_SPEC_lsp_tunv4 *f2a = &f2->filtlsp4; return (IN_ARE_ADDR_EQUAL(&f1a->lsptun_saddr,&inaddr_any) || IN_ARE_ADDR_EQUAL(&f2a->lsptun_saddr,&inaddr_any) || (IN_ARE_ADDR_EQUAL(&f1a->lsptun_saddr,&f2a->lsptun_saddr) && f1a->lsptun_lspid == f2a->lsptun_lspid) ); } default: /* Treat unknown type as not matching. * XXX This is actually a bug... each refresh with an * unknown sender template makes new path state...! */ return(0); } } /* Same, but second argument is SENDER_TEMPLATE. Really just * type matching. */ int match_sender_filter(FILTER_SPEC *f1, SENDER_TEMPLATE *f2) { return( match_filter(f1, (FILTER_SPEC *)f2) ); } int match_policy(POLICY_DATA *pdo1p, POLICY_DATA *pdo2p) { if (Obj_Length(pdo1p) != Obj_Length(pdo2p)) return(0); if (memcmp((char *)pdo1p, (char *)pdo2p, Obj_Length(pdo1p))) return 0; return(1); } /* * Session hash function */ int Sess_hashf(SESSION *session) { int i,size; u_int32_t n = 0,*lp; switch(Obj_CType(session)) { case ctype_SESSION_ipv4: case ctype_SESSION_ipv4GPI: size = sizeof(struct in_addr) / sizeof(n); lp = (u_int32_t *) &session->sess4_addr; break; #ifdef USE_IPV6 case ctype_SESSION_ipv6: case ctype_SESSION_ipv6GPI: size = sizeof(struct in6_addr) / sizeof(n); lp = (u_int32_t *) &session->sess6_addr; break; #endif /* USE_IPV6 */ case ctype_LSP_TUNNEL_IPv4: size = sizeof(struct in_addr) / sizeof(n); lp = (u_int32_t *)&session->sesslsp_addr; break; default: return(0); } for (i = 0;i < size; i++) n ^= *lp++; return(n % SESS_HASH_SIZE); } /* * Locate Session block for given SESSION object * Return 0 if no match, -1 if there is confusion about zero * ports, and the Session struct address otherwise. */ Session * locate_session_p(SESSION *sessp) { Session *destp; int hash = Sess_hashf(sessp); for (destp = session_hash[hash]; destp ; destp = destp->d_next) { if (session_eq_except_flags(destp->d_session,sessp)) return (destp); if (session_eq_except_port(destp->d_session,sessp)) return ((Session *) -1); } return (NULL); } /* locate_session(): Same as locate_session_p, except do not check * for confusion about zero ports; require exact match. */ Session * locate_session(SESSION *sessp) { Session *destp; int hash = Sess_hashf(sessp); for (destp = session_hash[hash]; destp ; destp = destp->d_next) { if (session_eq_except_flags(destp->d_session,sessp)) return (destp); } return (NULL); } /* * Create Session block for new session */ Session * make_session(SESSION *sessp) { Session *destp; int hash = Sess_hashf(sessp); int i; destp = (Session *) calloc(1, sizeof(Session)); if (!destp) { Log_Mem_Full("New session"); return(NULL); } /* * Initialize fields of Session structure. */ destp->d_PSB_list = NULL; /* no senders yet */ destp->d_session = (SESSION *)copy_object((Object_header *)sessp); Init_Object(&destp->d_timevalp, TIME_VALUES, TIME_VALUES_CTYPE) destp->d_timevalp.timev_R = 0; Init_Object(&destp->d_timevalr, TIME_VALUES, TIME_VALUES_CTYPE) destp->d_timevalr.timev_R = 0; destp->d_flags = 0; destp->d_LLB_listv = (void *) calloc(if_num, sizeof(void *)); if (!destp->d_LLB_listv) { free((char *) destp); return(NULL); } for (i = 0; i < if_num; i++) destp->d_LLB_listv[i] = NULL; /* * Insert new destination first in hast list */ destp->d_next = session_hash[hash]; session_hash[hash] = destp; return(destp); } /* * kill_session(): All senders and reservations for this Session have * been deleted; complete cleanup of the session and delete the * Session (session) block itself. */ int kill_session(Session *sessp) { Session **d; int hash = Sess_hashf(sessp->d_session); #if DEBUG int i; assert(!sessp->d_PSB_list && !sessp->d_RSB_list); for (i = 0; i < if_num; i++) assert(sessp->d_LLB_listv[i] == NULL); #endif /* DEBUG */ free(sessp->d_LLB_listv); free(sessp->d_session); /* Take dest off hash list, and then delete all its timer events. * Finally, free the control block. */ for (d = &session_hash[hash]; (*d) != NULL && (*d) != sessp; d = &((*d)->d_next)); assert(*d); *d = sessp->d_next; del_from_timer((char *) sessp, TIMEV_RESV); del_from_timer((char *) sessp, TIMEV_PATH); free((char *) sessp); return (1); } int scope_count(SCOPE *scope) { if (Obj_Class(scope) != class_SCOPE) return(0); switch(Obj_CType(scope)) { case ctype_SCOPE_list_ipv4: return(Obj_datalen(scope) / sizeof(SCOPE_list_ipv4)); #ifdef USE_IPV6 case ctype_SCOPE_list_ipv6: return(Obj_datalen(scope) / sizeof(SCOPE_list_ipv6)); #endif /* USE_IPV6 */ default: return(0); } } /* * Create new SCOPE object with specified number of slots. */ SCOPE * new_scope_obj(int count,u_char ctype) { int size; SCOPE *scp; switch(ctype) { case ctype_SCOPE_list_ipv4: size = sizeof(SCOPE_list_ipv4); break; #ifdef USE_IPV6 case ctype_SCOPE_list_ipv6: size = sizeof(SCOPE_list_ipv6); break; #endif /* USE_IPV6 */ case ctype_SCOPE_list_lspv4: size = sizeof(SCOPE_list_lspv4); break; default: return(NULL); } size = count * size + sizeof(Object_header); scp = (SCOPE *)malloc(size); assert(scp); Init_Var_Obj(scp,SCOPE,NULL,size); Obj_CType(scp) = ctype; return(scp); } #define INIT_SCOPE_LEN 64 #define MAX_SCOPE_LEN 65536 /* * scope_catf(): Catenate IP address from given FILTER_SPEC onto * existing SCOPE list, which is assumed to be ordered, * and return pointer to new SCOPE list. Create SCOPE * list if necessary. Ignore a duplicate address. */ void scope_catf(SCOPE ** scppp, FILTER_SPEC *filtp) { int N, L, ctype, addrsize; char *cp,*end,*addr; SCOPE *scpp, *new_scpp; switch (Obj_CType(filtp)) { case ctype_FILTER_SPEC_ipv4: ctype = ctype_SCOPE_list_ipv4; addrsize = sizeof(struct in_addr); addr = (char *) &filtp->filt4_srcaddr; break; case ctype_FILTER_SPEC_ipv4GPI: ctype = ctype_SCOPE_list_ipv4; addrsize = sizeof(struct in_addr); addr = (char *) &filtp->filtgpi4_srcaddr; break; #ifdef USE_IPV6 case ctype_FILTER_SPEC_ipv6: ctype = ctype_SCOPE_list_ipv6; addr = (char *) &filtp->filt6_srcaddr; addrsize = sizeof(struct in6_addr); break; case ctype_FILTER_SPEC_ipv6GPI: ctype = ctype_SCOPE_list_ipv6; addr = (char *) &filtp->filtgpi6_srcaddr; addrsize = sizeof(struct in6_addr); break; #endif /* USE_IPV6 */ case ctype_FILTER_SPEC_lsp_tunv4: ctype = ctype_SCOPE_list_lspv4; addr = (char *)&filtp->filtlsp4_saddr; addrsize = sizeof(struct in_addr); break; default: return; } scpp = *scppp; if (!scpp) { /* First time... set up object * We use the object length field to keep track of * the number of entries at present. The size of * malloc'd area is nearest power of 2 that is >= * this len, but at least INIT_SCOPE_LEN. */ scpp = new_scope_obj(INIT_SCOPE_LEN,ctype); if (!scpp) return; Obj_Length(scpp) = sizeof(Object_header); } else { if (Obj_CType(scpp) != ctype) return; /* Ignore a duplicate ip addr in list. */ end = ((char *) scpp) + Obj_Length(scpp); for (cp = (char *) Obj_data(scpp);cp < end; cp += addrsize) if (memcmp(cp,addr,addrsize) == 0) return; } /* Compute size of existing area */ L = Obj_Length(scpp); for (N = INIT_SCOPE_LEN; N < MAX_SCOPE_LEN; N <<= 1) if (N >= L) break; if (L + addrsize > N) { /* Overflow. Malloc a new object area of double size, * copy into it, and free original one. */ new_scpp = (SCOPE *) malloc(N+N); if (!new_scpp) { /* XXX ?? */ return; } memset(new_scpp, 0, N+N); memcpy(new_scpp, scpp, L); free(scpp); scpp = new_scpp; } memcpy(((char *) scpp) + Obj_Length(scpp),addr,addrsize); Obj_Length(scpp) += addrsize; *scppp = scpp; } /* * form_scope_union(): Form global union of all SCOPE lists for given session, * if it does not already exist, with local senders removed. * * Turn on scope bit in each matching PSB. */ int form_scope_union(Session *destp) { RSB *rp; PSB *sp; if (destp->d_flags & SESSF_HaveScope) return(0); for (rp = destp->d_RSB_list; rp != NULL; rp = rp->rs_next) { for (sp = destp->d_PSB_list ; sp != NULL; sp = sp->ps_next) { if (IsHopAPI(&sp->ps_phop)) continue; if (rp->rs_scope) { /* * Merge scope list into scope union, by turning * on scope bit in each matching PSB that is not * local API. (Doing linear search of PSBs, but * could use hash table) */ if (hop_in_scope(&sp->ps_phop, rp->rs_scope)) sp->ps_flags |= PSBF_InScope; } else { /* No scope list. Add to union all senders that * route to this RSB. */ if (IsRoutePSB2nhop(destp, sp, &rp->rs_rsvp_nhop)) sp->ps_flags |= PSBF_InScope; } } } destp->d_flags |= SESSF_HaveScope; return(0); } /* * clear_scope_union(): Delete existing scope union (ie turn off scope * flag bits in all PSBs. Union will be recomputed when needed. * * This is called when a new PSB is created, an PSB is deleted, a new * RSB is created, an RSB with SCOPE list is deleted, or the SCOPE list * of an existing RSB changes. */ void clear_scope_union(Session *destp) { PSB *sp; if ((destp->d_flags & SESSF_HaveScope) == 0) return; for (sp = destp->d_PSB_list ; sp != NULL; sp = sp->ps_next) sp->ps_flags &= ~PSBF_InScope; destp->d_flags &= ~SESSF_HaveScope; } /* * Check to see if two SCOPE's are equal * * return 1 if true, and 0 otherwise */ int match_scope(SCOPE *s1, SCOPE *s2) { int size; char *cp1,*end1,*cp2,*end2; if (!s1) { return(!s2); } else if (!s2) return(0); if (Obj_CType(s1) != Obj_CType(s2)) return(0); if (Obj_Length(s1) != Obj_Length(s2)) return(0); size = Obj_datalen(s1) / scope_count(s1); end1 = ((char *) s1) + Obj_Length(s1); end2 = ((char *) s2) + Obj_Length(s2); for (cp1 = (char *) Obj_data(s1);cp1 < end1; cp1 += size) { for (cp2 = (char *) Obj_data(s2);cp2 < end2; cp2 += size) if (memcmp(cp1,cp2,size) == 0) break; if (cp2 >= end2) return(0); } return(1); } /* * format functions for building the explicit route object */ #ifdef EX_ROUTE /* * creates a new EXROUTE Object witch specified number of slots */ EXROUTE * new_exroute_obj(int count,u_char ctype) { int size; EXROUTE *exp; switch(ctype) { case ctype_EX_ROUTE_IPv4: size = sizeof(EX_ROUTE_IPv4); break; #ifdef USE_IPV6 case ctype_EXROUTE_ipv6: size = sizeof(EX_ROUTE_IPv6); break; #endif /* USE_IPV6 */ default: return(NULL); } size = count * size + sizeof(Object_header); exp = (EXROUTE *)malloc(size); assert(exp); Obj_Length(exp) = size; Obj_Class(exp) = class_POLICY_DATA; Obj_CType(exp) = ctype; return(exp); } /* * catenates subobjects together to one explicit route object (only for ip now) MZ */ #define EXROUTE_LEN 10 void exroute_cat(EXROUTE ** exppp, EX_ROUTE_IPv4 * ex_route) { int N, L, ctype, addrsize; char *cp,*end,*addr; EXROUTE *expp, *new_expp; ctype = ctype_EX_ROUTE_IPv4; addrsize = 8; addr = (char *)ex_route; expp = *exppp; if (!expp) { /* First time... set up object * We use the object length field to keep track of * the number of entries at present. The size of * malloc'd area is nearest power of 2 that is >= * this len, but at least INIT_SCOPE_LEN. */ expp = new_exroute_obj(EXROUTE_LEN, ctype); if (!expp) return; Obj_Length(expp) = sizeof(Object_header); } else { if (Obj_CType(expp) != ctype) return; /* Ignore a duplicate ip addr in list. */ end = ((char *) expp) + Obj_Length(expp); for (cp = (char *) Obj_data(expp);cp < end; cp += addrsize) if (memcmp(cp,addr,addrsize) == 0) return; } /* Compute size of existing area */ L = Obj_Length(expp); for (N = EXROUTE_LEN; N < MAX_SCOPE_LEN; N <<= 1) if (N >= L) break; if (L + addrsize > N) { /* Overflow. Malloc a new object area of double size, * copy into it, and free original one. */ new_expp = (EXROUTE *) malloc(N+N); if (!new_expp) { /* XXX ?? */ return; } memset(new_expp, 0, N+N); memcpy(new_expp, expp, L); free(expp); expp = new_expp; } memcpy(((char *) expp) + Obj_Length(expp),addr,addrsize); Obj_Length(expp) += addrsize; *exppp = expp; } #endif