/***************************************************************************** @(#) $Id: mtp_route.c,v 0.7.2.9 2000/10/05 10:23:04 brian Exp $ ----------------------------------------------------------------------------- Copyright (C) 1997, 1998, 1999, 2000 Brian Bidulock All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Last Modified $Date: 2000/10/05 10:23:04 $ by $Author: brian $ ----------------------------------------------------------------------------- $Log: mtp_route.c,v $ Revision 0.7.2.9 2000/10/05 10:23:04 brian Some work on link management and test. Revision 0.7.2.9 2000/10/03 00:53:54 brian Some work on link management and test. Revision 0.7.2.8 2000/09/21 05:19:07 brian Removed some leftover files. Added changeback to mtp_sm.c. Revision 0.7.2.7 2000/09/18 09:36:54 brian Corrections to compile. Revision 0.7.2.6 2000/09/18 09:33:44 brian A bunch of work in state machines. Revision 0.7.2.5 2000/09/13 13:47:06 brian A lot of work in state machine: haven't check compile yet, just wanting to save work for later. Revision 0.7.2.4 2000/09/12 18:45:22 brian Lots of work within the MTP state machine files. Revision 0.7.2.3 2000/09/12 04:57:30 brian Still working on state machine: still compiles. Revision 0.7.2.2 2000/09/11 23:41:53 brian Working on MTP state machine: still compiles. Revision 0.7.2.1 2000/09/11 20:20:48 brian Cleaned up routing structure. Revision 0.7 2000/09/11 14:07:08 brian Added files for socket stuff and routing. Revision 0.7.2.1 2000/09/11 07:21:34 brian Doing a lot of work in socket code files. Revision 0.7 2000/09/07 11:12:13 brian Initial import of OpenSS7. Revision 1.1.1.1 2000/09/05 11:00:20 brian Initial import of new OpenSS7 stack for Linux. *****************************************************************************/ static char const ident[] = "$Name: Prerelease1 $($Revision: 0.7.2.9 $) $Date: 2000/10/05 10:23:04 $"; #include #include #include #include #include "af_ss7.h" #include "mtp_route.h" #include "mtp_sm.h" #include "../../include/linux/ss7link.h" static struct ss7_routeset * ss7rtab[SS7RTAB_SIZE] = { NULL, }; /* * Some support functions for deteling items in routing * structures. */ struct ss7_routeset *ss7_all_routesets = NULL; struct ss7_route *ss7_all_routes = NULL; struct ss7_linkset *ss7_all_linksets = NULL; /* * Delete a route pair or regular route. Route pairs are lined up in memory * and are really a two element array of routes. */ static __inline void ss7_del_route(struct ss7_route *rt) { static int recur = 0; struct ss7_route **r; if (!recur && rt->other && rt->other>rt) { recur++; ss7_del_route(rt->other); } for ( r = &ss7_all_routes; *r && *r != rt; r = &(*r)-> next ); *r = rt-> next; for ( r = &rt->rset ; *r && *r != rt; r = &(*r)-> rset ); *r = rt-> rset; if (rt->routeset && rt->routeset->routes == rt) { if (!rt->rset || rt->rset == rt) rt->routeset->routes = NULL; else rt->routeset->routes = rt->rset; } r = &rt->lset; while ( (*r) && (*r)!=rt ) { r = &(*r)->lset; } (*r)=rt->lset; if (rt->linkset && rt->linkset->routes == rt) { if (!rt->lset || rt->lset == rt) rt->linkset->routes = NULL; else rt->linkset->routes = rt->lset; } if (!recur && rt->other && rt->otherother); } if (rt->other && rt->other>rt) kfree(rt); recur = 0; } static __inline void ss7_del_linkset(struct ss7_linkset *ls) { struct ss7_linkset **s; s = &ss7_all_linksets; while ( (*s) && (*s)!=ls ) { s=&(*s)->next; } (*s)=ls->next; while (ls->routes) ss7_del_route(ls->routes); kfree(ls); } /* * Returns a pointer to the pointer to the entry which has and address which * matches `addr' and an address mask which matches `mask' and which has all * the bits set in flags and type which are set in `flags' and `type'. If it * fails it returns a pointer to NULL at a location where it is possible to * insert the a new item matching the criteria. */ static __inline struct ss7_routeset **ss7_rtab_get(__u32 addr, __u32 mask, __u8 flags, __u8 type) { struct ss7_routeset **rs; for ( rs = &ss7rtab[(addr+(addr>>8)+(addr>>16))&0xff]; (*rs) && ( (*rs)->addr!=addr || (*rs)->mask!=mask || ( flags && ((*rs)->flags ^ flags) & flags ) || ( flags && ((*rs)->type ^ type ) & type ) ); rs=&(*rs)->collide ); return rs; } /* * Returns a pointer to the entry which has an address which matches `addr' * and an address mask which matches `mask' and which has all the bits set in * flags and type which are set in `flags' and `type'. If no such entry * exists, space is allocated for such an entry and the routeset entry is * intialized and the pointer returned. If no entry exists and memory * allocation fails, it returns NULL, otherwise it always succeeds. */ static __inline struct ss7_routeset *ss7_rtab_add(struct ss7_addr saddr, __u32 mask, __u8 flags, __u8 type) { __u32 addr = saddr.s_addr & mask; struct ss7_routeset **rs; if ( (rs = ss7_rtab_get(addr,mask,0,0)) != NULL || (*rs = kmalloc(sizeof(**rs),GFP_ATOMIC)) ) { struct ss7_routeset *r = *rs; memset(r,0x00,sizeof(*r)); r->addr = addr; r->mask = mask; r->type = type; r->flags = flags|SS7_RS_INHIBITED; /* FIXME: not required */ r->next = ss7_all_routesets; ss7_all_routesets = r; } return (*rs); /* NULL on failure */ } /* * Lookup a linkset by its local and adjacent routeset pointers and return a * newly formed route attached to the remote routeset and the linkset. * Normally called when receiving TFA for a new destination during MTP * restart. Returns NULL on failure (no linkset for loc/adj pair). */ extern __inline struct ss7_route *ss7_add_route( struct ss7_routeset *rem, struct ss7_routeset *loc, struct ss7_routeset *adj) { struct ss7_route *r = NULL; struct ss7_linkset *ls; for (ls = ss7_all_linksets; ls; ls = ls->next) if ( ( ls->local == loc ) && ( ls->adjacent == adj ) ) break; if (!ls) return r; if (ls->other) { /* we really want a route pair */ if (!(r = kmalloc(2*sizeof(*r),GFP_ATOMIC))) return r; memset(r,0x00,2*sizeof(*r)); r->flags = SS7_RT_INHIBITED; r->priority = ls->other->type; r->linkset = ls->other; r->routeset = rem; r->rset = rem->routes; rem->routes = r; r->lset = ls->other->routes; ls->other->routes = r; r->other = r+1; r->other->other = r; r->next = ss7_all_routes; ss7_all_routes = r; r = r+1; } else { if (!(r = kmalloc(sizeof(*r),GFP_ATOMIC))) return r; memset(r,0x00,sizeof(*r)); } { r->flags = SS7_RT_INHIBITED; r->priority = ls->type; r->linkset = ls; r->routeset = rem; r->rset = rem->routes; rem->routes = r; r->lset = ls->routes; ls->routes = r; r->next = ss7_all_routes; ss7_all_routes = r; } return r; } static __inline void ss7_del_routeset(struct ss7_routeset *rs) { struct ss7_routeset **r; r = &ss7_all_routesets; while ( (*r) && (*r)!=rs ) { r = &(*r)->next; } (*r)=rs->next; while (rs->routes) ss7_del_route(rs->routes); /* remove from hash tables */ r = ss7_rtab_get(rs->addr,rs->mask,0,0); if (r && (*r)) (*r)=rs->collide; kfree(rs); } extern __inline struct ss7_routeset *ss7_rt(struct ss7_addr saddr) { struct ss7_routeset **rsp, *rt=NULL; if ( !(rsp = ss7_rtab_get(saddr.s_addr,SS7_RS_MASK_MEMBER ,0,0)) && !(rt = *rsp) ) if ( !(rsp = ss7_rtab_get(saddr.s_addr,SS7_RS_MASK_CLUSTER,0,0)) && !(rt = *rsp) ) if ( !(rsp = ss7_rtab_get(saddr.s_addr,SS7_RS_MASK_NETWORK,0,0)) && !(rt = *rsp) ); return rt; } extern __inline struct ss7_routeset *ss7_rs_add(struct ss7_addr saddr, __u8 flags, __u8 type) { __u32 mask; if ( (type & SS7_RST_SCOPE) == SS7_RST_MEMBER ) { mask = SS7_RS_MASK_MEMBER ; } else if ( (type & SS7_RST_SCOPE) == SS7_RST_CLUSTER ) { mask = SS7_RS_MASK_CLUSTER; } else if ( (type & SS7_RST_SCOPE) == SS7_RST_NETWORK ) { mask = SS7_RS_MASK_NETWORK; } else { mask = SS7_RS_MASK_MEMBER; type = (type & ~SS7_RST_SCOPE) | SS7_RST_MEMBER; } return ss7_rtab_add(saddr,mask,flags,type); } extern __inline ss7_routeset *ss7_rt_type(struct ss7_addr saddr, __u8 flags, __u8 type) { __u32 mask; struct ss7_routeset **rsp; if ( (type & SS7_RST_SCOPE) == SS7_RST_MEMBER ) { mask = SS7_RS_MASK_MEMBER ; } else if ( (type & SS7_RST_SCOPE) == SS7_RST_CLUSTER ) { mask = SS7_RS_MASK_CLUSTER; } else if ( (type & SS7_RST_SCOPE) == SS7_RST_NETWORK ) { mask = SS7_RS_MASK_NETWORK; } else { mask = SS7_RS_MASK_MEMBER; type = (type & ~SS7_RST_SCOPE) | SS7_RST_MEMBER; } if ( !(rsp = ss7_rtab_get(saddr.s_addr,mask,flags,type)) ) return NULL; return (*rsp); } /* used to bind sockets in af_ss7.c */ extern __inline unsigned ss7_addr_type(struct ss7_addr saddr) { struct ss7_routeset *rt = ss7_rt(saddr); if ( !rt ) return 0; return ( rt->type ); } /* * Limit ourselves from a routeset down to a route. Route * lists associated with routesets are sorted by priority, * however, we will always shift from a congested route to * a non-congested route regardless of priority. * * This will find the highest priority usable route with * the least congestion. The selected route will not be * via a restarting SP. * * Caller must check for controlled reroute on RESTRICTED * routes. */ extern __inline struct ss7_route *ss7_select_rt(struct ss7_routeset *rs) { struct ss7_route *i; struct ss7_route *rl = rs->routes; for (i=rl;i;i=i->rset) if (!(i->flags&SS7_RT_DONTUSE)) rl = i; if (rl && !(rl->flags&SS7_RT_DONTUSE)) return rl; return NULL; } /* * Links selection method for selecting a link within a * combined linkset on the basis of SLS as provided. Does * not update last in sequence to preserve loadsharing. */ extern __inline struct ss7_link *ss7_select_link(struct ss7_route *rt, unsigned char sls) { unsigned set, lnk, i; struct ss7_linkset *ls1 = NULL, *ls0 = rt->linkset; struct ss7_link *l = NULL; if (rt->linkset->type==SS7_LS_TYPE_A) { set=(sls>>1)&0x1; lnk = ((sls>>1)&0x6)|(sls&0x1); } else { set=sls&0x1; lnk = (sls>>1); } if (ls0->other) { if (ls0->index^set) { ls0=ls0->other; ls1=ls0->other; } } if (!l && ls0) { l = ls0->links[lnk]; if (!l) for (i=0;i<8;i++) if ((l=ls0->links[lnk^i])) break; } if (!l && ls1) { l = ls1->links[lnk]; if (!l) for (i=0;i<8;i++) if ((l=ls1->links[lnk^i])) break; } return l; } /* * Link selection method for loadsharing within a route * (between links and linksets of a combined linkset). * Selects the next linkset in a loadsharing pattern. */ extern __inline struct ss7_link *ss7_select_link_ls(struct ss7_route *rt) { rt->last = (rt->last+1)&0xf; return ss7_select_link(rt,rt->last); } /* * This function is responsible for adding or moving an * existing link to a linkset based on its local point * code, remote point code and SLS value. This function is * called at ioctl so it returns error codes. */ extern int ss7_move_link(struct ss7_link *link, struct ss7_routeset *local, struct ss7_routeset *adjacent, unsigned char sls) { /* * FIXME: make this function do something. It must go * searching for the adjacent point code in the local * routeset's links looking for a match, and then * checking sls position. If the link already has an * SLS position it must move it, otherwise add it. */ return -EINVAL; } /* * Discovers what the address of the signalling point * adjacent to the link that the device which generated * this sk_buff is. That is the adjacent signaling point * code for the message. Returns 0 or error. */ extern __inline __u32 ss7_get_adjacent(struct sk_buff *skb) { struct ss7_iface *iface; struct ss7_link *link; if ( ( !skb ) || ( !skb->dev ) || ( !skb->dev->ss7_ptr ) ) return (0); iface = (struct ss7_iface *)skb->dev->ss7_ptr; if ( iface->dev != skb->dev ) return (0); link = &iface->link; if ( !link->linkset || !link->linkset->adjacent ) return (0); return (link->linkset->adjacent->addr); } void ss7_rtab_init(void) { memset(&ss7rtab,0x0,sizeof(ss7rtab)); } /* * * SS7 MTP Level 3 Routing Tables and routines. * * */ #if 0 static int ss7_add_route(ss7_address *address, struct device *dev); static void ss7_remove_route(struct ss7_route *ss7_route); static int ss7_del_route(ss7_address *address, struct device *dev); static int ss7_route_device_down(struct device *dev); static device *ss7_iface_get(char *devname); /* * Find a device given an SS7 address. */ struct device* ss7_get_route(ss7_address *addr); #endif /* * Handle the ioctls that control the routing functions. */ int ss7_route_ioctl(unsigned int cmd, void *arg) { #if 0 switch (cmd) { case MTPGROUTESET: case MTPGROUTE: case MTPGLINKSET: case MTPGRSF: case MTPGRTF: case MTPGLSF: case MTPSROUTESET: case MTPSROUTE: case MTPSLINKSET: case MTPSRSF: case MTPSRTF: case MTPSLSF: default: #endif return -ENOIOCTLCMD; #if 0 } #endif }; #if 0 int ss7_routes_get_info(char *buffer, char **start, off_t offset, int length, int dummy); #endif /* * Release all memory associated with SS7 routing * structures */ void ss7_rtab_free(void) { volatile struct ss7_routeset *rs; volatile struct ss7_linkset *ls; while ((rs = ss7_all_routesets)) ss7_del_routeset((struct ss7_routeset *)rs); while ((ls = ss7_all_linksets )) ss7_del_linkset ((struct ss7_linkset *)ls); };