/***************************************************************************** @(#) $Id: af_ss7.c,v 0.7.2.5 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: af_ss7.c,v $ Revision 0.7.2.5 2000/10/05 10:23:04 brian Some work on link management and test. Revision 0.7.2.5 2000/10/03 00:53:54 brian Some work on link management and test. Revision 0.7.2.4 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.3 2000/09/11 20:20:47 brian Cleaned up routing structure. Revision 0.7.2.2 2000/09/11 14:08:18 brian Remove and updated some files for better structure. Most compile now... 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.2 2000/09/07 10:51:51 brian Got these files going. Revision 1.1.1.1 2000/09/05 11:00:20 brian Initial import of new OpenSS7 stack for Linux. *****************************************************************************/ /* * This is the protocol routines for the entire SS7 * protocol. It is repsonsible for initialization of the * SS7 protocol in the kernel. This is a separate module * which must be loaded before other ss7 modules (link, * net, sccp) are loaded. It is responsible for * registering the protocol options and socket creation * functions and the device notifiers for the SS7 stack. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_ss7.h" #include "mtp.h" #include "mtp_route.h" #include "sccp.h" #include "../../include/linux/ss7link.h" #ifdef CONFIG_KMOD #include #endif static struct proto_ops mtp_ops; static struct proto_ops sccp_cl_ops; static struct proto_ops sccp_co_ops; /* * Set socket options on any ss7 socket. */ static int ss7_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { struct sock *sk = sock->sk; if (sk->prot->setsockopt==NULL) return (-EOPNOTSUPP); return sk->prot->setsockopt(sk,level,optname,optval,optlen); } static int ss7_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; if (sk->prot->getsockopt==NULL) return (-EOPNOTSUPP); return sk->prot->getsockopt(sk,level,optname,optval,optlen); } static int ss7_listen(struct socket *sock, int backlog) { (void)&ss7_listen; (void)sock; (void)backlog; return (-EOPNOTSUPP); /* use later for SCCP class 2 and 3 */ } /* * A new socket has been created with the socket() command * by the user and they have specified PF_SS7 as the * protocol family. The `protocol' argument normally has * the IEEE 802.3 network order protocol type for PACKET * sockets. To use the services of the PACKET sockets, * this number must be assigned. Later we might want to * send SS7 over Ethernet (helluva concept!) so we might * want to pick an 802.3 number. Experimental numbers are * 0101 thru 01FF. We need the FF on the end for the * SOCK_PACKET code in Linux? I went ahead and assigned an * ETH_P_SS7 in with the other dummy * types so that packet taps will work correctly. The type * ETH_P_SS7 should be assigned by the driver to their * sk_buff's so that the packet redirector works. But * let's not use that here: we can use that for the * packet_socket(). * * Here, since we are in AF_SS7/PF_SS7 and not * AF_INET/PF_INET we can do whatever we would like with * the protocol field. I will define the protocol fields * in */ static int ss7_create(struct socket *sock, int protocol) { struct sock *sk; int err = 0; int socktype = sock->type; struct proto *prot; (void)&ss7_create; if (socktype == SOCK_PACKET) { if (net_families[PF_PACKET]==NULL) { #if defined(CONFIG_KMOD) && defined(CONFIG_PACKET_MODULE) char module_name[30]; sprintf(module_name, "net-pf-%d", PF_PACKET); request_mdoule(module_name); #endif if (net_families[PF_PACKET] == NULL) return -ESOCKTNOSUPPORT; } return net_families[PF_PACKET]->create(sock, protocol); } sock->state = SS_UNCONNECTED; if ((sk = sk_alloc(PF_SS7, GFP_KERNEL, 1)) == NULL) return -ENOBUFS; MOD_INC_USE_COUNT; switch (protocol) { case 0: protocol = SS7_PROTO_NTWK; /* assume she wanted MTP */ case SS7_PROTO_NTWK: switch (socktype) { case SOCK_NTWK_ISEQ: case SOCK_NTWK_NSEQ: prot = &mtp_prot; sock->ops = &mtp_ops; break; case SOCK_NTWK_RAW: /* deal with raw later */ default: err = -ESOCKTNOSUPPORT; break; } break; case SS7_PROTO_SCCP: switch (socktype) { case SOCK_SCCP_CC_0: case SOCK_SCCP_CC_1: prot = &sccp_prot; sock->ops = &sccp_cl_ops; break; case SOCK_SCCP_CC_2: case SOCK_SCCP_CC_3: prot = &sccp_prot; sock->ops = &sccp_co_ops; break; default: err = -ESOCKTNOSUPPORT; break; } break; default: err = -EPROTONOSUPPORT; break; } if (err) { sk_free(sk); MOD_DEC_USE_COUNT; return err; } sock_init_data(sock,sk); sk->state = 0; sk->destruct = NULL; sk->zapped = 0; sk->family = PF_SS7; sk->protocol = protocol; sk->prot = prot; sk->backlog_rcv = prot->backlog_rcv; if (sk->prot->init) { err = sk->prot->init(sk); if (err) { if (sk->prot->destroy) sk->prot->destroy(sk); sk_free(sk); MOD_DEC_USE_COUNT; return (err); } } return (0); } /* * This socket has just be released. Gee, we just made it! * :) */ static int ss7_release(struct socket *sock, struct socket *peersock) { struct sock *sk = sock->sk; if (sk) { long timeout; if (sock->state != SS_UNCONNECTED) sock->state = SS_DISCONNECTING; sk->state_change(sk); /* * If linger is set, we don't return until the * close is complete. Otherwise we return * immediately. The actually closing is done the * same either way. * * If the close is due to the process exiting, we * never linger.. */ timeout = 0; if (sk->linger && !(current->flags & PF_EXITING)) { timeout = HZ * sk->lingertime; if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; } sock->sk = NULL; sk->socket = NULL; /* * Call the protocol's release mechanism. */ sk->prot->close(sk, timeout); } return(0); } /* * This bind is rather generic. Unlike IP, SS7 has * different bind requirements for MTP and SCCP, so the * specific binding has been moved down to the prot * methods. */ static int ss7_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk=sock->sk; unsigned short snum; int err = 0; /* If the socket has its own bind function then use * it. (RAW) */ if(sk->prot->bind) if ((err = sk->prot->bind(sk, uaddr, addr_len))) return (err); /* * Ask the protocol (in this case MTP) whether we * can bind to this port number. In our case this * checks the ss7mtp_cb associated with the address * (sk->saddr) and check whether the user part is * vacant. */ if (sk->prot->get_port(sk,snum) != 0) return -EADDRINUSE; sk->zapped = 0; sk->sport = sk->num; /* we don't really have a short */ sk->daddr = 0; sk->dport = 0; /* * Ask the protocol to place this into its hash * list. In the case of MTP we put the sk address * against the user part in the ss7mtp_cb * associated with the routeset for (sk->saddr). */ sk->prot->hash(sk); /* * Add it to the list of SS7 socket in ss7_prot. */ add_to_prot_sklist(sk); sk->dst_cache=NULL; return(0); } /* * Connect the socket tot he address specified. This is * roughed in only for later SCCP Class 2 and 3 use. We do * not support connect in the sccp_prot yet. */ static int ss7_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; int err; (void)&ss7_connect; if (sk->prot->connect == NULL) return (-EOPNOTSUPP); err = sk->prot->connect(sk, (struct sockaddr *)uaddr, addr_len); if (err < 0) return err; return (0); } /* * Accept a pending connection. This is only useful for * SCCP connection classes 2 and 3 which are not supported * yet. */ static int ss7_accept(struct socket *sock, struct socket *newsock, int flags) { struct sock *sk1 = sock->sk, *sk2; struct sock *newsk = newsock->sk; int err; (void)&ss7_accept; err = -EOPNOTSUPP; if (sk1->prot->connect == NULL) return err; if ((sk2 = sk1->prot->accept(sk1,flags))==NULL) { err = sock_error(sk1); return err; } newsock->sk = sk2; sk2->socket = newsock; newsk->socket = NULL; destroy_sock(newsk); newsock->state = SS_CONNECTED; return 0; } /* * Get the address associated with this socket. If we get * our name, we will return the SS7 address (Point Code) of * the local size of the connection with is the MTP Point * Code of the protoco to which we were bound. The remote * side of the connection only makes sense for some MTP * user parse or on a link-by-link basis. */ static int ss7_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sock *sk = sock->sk; struct sockaddr_ss7 *spc = (struct sockaddr_ss7 *)uaddr; spc->ss7_family = AF_SS7; if (peer) { return -ENOTCONN; /* no SCCP class 2 or 3 support yet */ } else { __u32 addr = sk->rcv_saddr; if (!addr) addr = sk->saddr; spc->ss7_si = sk->sport; spc->ss7_dpc.s_addr = addr; } *uaddr_len = sizeof(*spc); return(0); } /* * Receive a message for the socket and deliver it to the * socket user. */ static int ss7_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; int addr_len = 0; int err; if (sock->flags & SO_ACCEPTCON) return(-EINVAL); if (sk->prot->recvmsg == NULL) return(-EOPNOTSUPP); if (!sk->num) return(-EAGAIN); err = sk->prot->recvmsg(sk, msg, size, flags&MSG_DONTWAIT, flags&~MSG_DONTWAIT, &addr_len); if (err >= 0) msg->msg_namelen = addr_len; return err; } /* * Send a message to the lower protocol layer for ultimate * transmission at the network device. */ static int ss7_sendmsg(struct socket *sock, struct msghdr *msg, int size, struct scm_cookie *scm) { struct sock *sk = sock->sk; if (sk->shutdown & SEND_SHUTDOWN) { if (!(msg->msg_flags&MSG_NOSIGNAL)) send_sig(SIGPIPE, current, 1); return(-EPIPE); } if (sk->prot->sendmsg == NULL) return(-EOPNOTSUPP); if (sk->err) return sock_error(sk); if (!sk->num) return(-EAGAIN); return sk->prot->sendmsg(sk, msg, size); } /* * Shut down the socket in one or both directions; however, * neither is really applicable to SS7 MTP, here for SCCP * later. */ static int ss7_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; (void)&ss7_shutdown; how++; if ((how & ~SHUTDOWN_MASK) || how==0) return(-EINVAL); if (!sk) return(-ENOTCONN); return (-ENOTCONN); #if 0 if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED) sock->state = SS_CONNECTED; if (!tcp_connected(sk->state)) return(-ENOTCONN); sk->shutdown |= how; if (sk->prot->shutdown) sk->prot->shutdown(sk, how); sk->state_change(sk); return(0); #endif } /* * Simply chain poll requests on to the protocol layer and * let them deal with them. */ static unsigned int ss7_poll(struct file * file, struct socket *sock, poll_table *wait) { (void)file; (void)wait; return (sock->sk->prot->poll) ? sock->sk->prot->poll(file, sock, wait) : 0; } /* * Device specific ioctls derived from net/ipv4/devinet.c */ static int devss7_ioctl(unsigned int cmd, void *arg) { struct ifreq ifr; struct sockaddr_ss7 *spc = (struct sockaddr_ss7 *)&ifr.ifr_addr; struct ss7_routeset *rs_lcl = NULL; struct ss7_routeset *rs_adj = NULL; struct ss7_iface *iface; struct device *dev; /* * Fetch the caller's info block into kernel space */ if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) return -EFAULT; ifr.ifr_name[IFNAMSIZ-1] = 0; #ifdef CONFIG_KMOD dev_load(ifr.ifr_name); #endif if ((dev = dev_get(ifr.ifr_name)) == NULL) return -ENODEV; if ((iface = (struct ss7_iface *)dev->ss7_ptr)==NULL) return -ENODEV; switch(cmd) { case SIOCGIFADDR: /* Get interface address */ if (!iface->link.linkset) return -EADDRNOTAVAIL; if (!(rs_adj = iface->link.linkset->adjacent)) return -EADDRNOTAVAIL; if (!(rs_lcl = iface->link.linkset->local)) return -EADDRNOTAVAIL; memset(spc, 0, sizeof(*spc)); spc->ss7_family = AF_SS7; spc->ss7_opc.s_addr = rs_lcl->addr; spc->ss7_dpc.s_addr = rs_adj->addr; spc->ss7_sls = iface->link.index; if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; return(0); case SIOCSIFFLAGS: if (!capable(CAP_NET_ADMIN)) return -EACCES; return dev_change_flags(dev,ifr.ifr_flags); case SIOCSIFADDR: /* Set interface address (and family) */ if (spc->ss7_family != AF_SS7) return -EINVAL; if (!(rs_lcl = ss7_rt_type(spc->ss7_opc, 0, SS7_RST_LOCAL ))) return -EADDRNOTAVAIL; if (!(rs_adj = ss7_rt_type(spc->ss7_dpc, 0, SS7_RST_ADJACENT))) return -EADDRNOTAVAIL; return ss7_move_link(&iface->link,rs_lcl,rs_adj,spc->ss7_sls); default: return -EINVAL; } return 0; } /* * ioctl() calls you can issue on an SS7 socket. Process * some, pass some on the the socket. Many are not * applicatble to SS7. We define some new ones but reuse * as many of the IP ones as possible. * * Note: this function copied straight from * net/ipv4/af_inet.c and modified to meet SS7 needs. * */ static int ss7_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; int err; int pid; switch(cmd) { case FIOSETOWN: case SIOCSPGRP: err = get_user(pid, (int *) arg); if (err) return err; if (current->pid != pid && current->pgrp != -pid && !capable(CAP_NET_ADMIN)) return -EPERM; sk->proc = pid; return(0); case FIOGETOWN: case SIOCGPGRP: return put_user(sk->proc, (int *)arg); case SIOCGSTAMP: if(sk->stamp.tv_sec==0) return -ENOENT; err = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)); if (err) err = -EFAULT; return err; case SIOCADDRT: case SIOCDELRT: case SIOCRTMSG: /* * SS7 has a different routing * table mechanism and a different * routing table than IP. We * redirect this to the right * place. */ return(ss7_route_ioctl(cmd,(void *) arg)); case SIOCDARP: case SIOCGARP: case SIOCSARP: case SIOCDRARP: case SIOCGRARP: case SIOCSRARP: /* * SS7 doesn't do ARP (yet: I'm * workin' on it). */ return -EINVAL; case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: /* * SS7 doesn't do broadcast or * netmask at interface level. */ return -EINVAL; case SIOCGIFADDR: /* Reuse for local Point Code for links */ case SIOCSIFADDR: /* Reuse for local Point Code for links */ case SIOCGIFDSTADDR: /* Reuse for remote Point Code for links */ case SIOCSIFDSTADDR: /* Reuse for remote Point Code for links */ case SIOCSIFPFLAGS: /* Private flags: we will use these */ case SIOCGIFPFLAGS: /* Private flags: we will use these */ case SIOCSIFFLAGS: /* Interface flags like up and down */ /* * Send them to the right place. */ return(devss7_ioctl(cmd,(void *) arg)); /* ss7_dev.c */ case SIOCGIFBR: case SIOCSIFBR: case SIOCADDDLCI: case SIOCDELDLCI: /* * SS7 isn't configured for bridges * or DLCIs ...yet! */ return -EINVAL; default: if ((cmd >= SIOCDEVPRIVATE) && (cmd <= (SIOCDEVPRIVATE + 15))) return(dev_ioctl(cmd,(void *) arg)); /* that was the generic dev_ioctl * from core/dev.c */ if (sk->prot->ioctl==NULL || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD) return(dev_ioctl(cmd,(void *) arg)); /* that was the generic dev_ioctl * from core/dev.c */ return err; } /*NOTREACHED*/ return(0); } /* * These are the protocol operations for socket<->protocol * communication as distinguished from protocol<->protocol * communication and the prototocol<->network interface * communication. This is the BSD way. Many stacks in * Linux do not follow this, but I will :) * */ static struct proto_ops mtp_ops = { PF_SS7, sock_no_dup, /* makse no sense for MTP */ ss7_release, /* release the socket */ ss7_bind, /* bind the socket to an SS7 address */ sock_no_connect, /* makes no sense for MTP */ sock_no_socketpair, /* makes no sense for MTP */ sock_no_accept, /* makes no sense for MTP */ ss7_getname, /* only the local address makes sense for MTP */ ss7_poll, /* poll for readable/writable */ ss7_ioctl, /* ioctls for MTP not link */ sock_no_listen, /* makse no sense for MTP */ sock_no_shutdown, /* only both sides makes sense for MTP */ ss7_setsockopt, /* O.K, but a lot of standard ones no good */ ss7_getsockopt, /* O.K, but a lot of standard ones no good */ sock_no_fcntl, /* is it not a file device */ ss7_sendmsg, /* how we get out messages */ ss7_recvmsg /* how we get out messages */ }; #ifdef NEED_SCCP /* * I don't have anything defined for SCCP sockets yet, I just thought that * I would rough in the socket code for SCCP while I was in here doing * MTP. */ static struct proto_ops sccp_cl_ops { PF_SS7, sock_no_dup, ss7_release, ss7_bind, sock_no_connect, sock_no_socketpair, sock_no_accept, ss7_getname, ss7_poll, ss7_ioctl, sock_no_listen, sock_no_shutdown, ss7_setsockopt, ss7_getsockopt, sock_no_fcntl, ss7_sendmsg, ss7_recvmsg }; static struct proto_ops sccp_co_ops { PF_SS7, sock_no_dup, ss7_release, ss7_bind, ss7_connect, sock_no_socketpair, ss7_accept, ss7_getname, ss7_poll, ss7_ioctl, ss7_listen, ss7_shutdown, ss7_setsockopt, ss7_getsockopt, sock_no_fcntl, ss7_sendmsg, ss7_recvmsg }; static struct net_proto_family ss7_family_ops = { PF_SS7, ss7_create }; #ifdef CONFIG_PROC_FS /* * I need to put some proc filesystem definitions in here * so that the user can query information. Some stuff like * netstat_get_info, afss7_get_info, mtp_get_info, * sccp_get_info. */ static struct proc_dir_entry proc_ss7_mtp = { PROC_NET_SS7_MTP, 3, "mtp", /* defined in */ S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, mtp_get_info /* defined in "proc.c" */ }; static struct proc_dir_entry proc_ss7_sccp = { PROC_NET_SS7_SCCP, 4, "sccp", /* defined in */ S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, sccp_get_info /* defined in "proc.c" */ }; static struct proc_dir_entry proc_ss7_sockstat = { PROC_NET_SS7_SOCKSTAT, 8, "sockstat", /* defined in */ S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, afss7_get_info /* defined in "proc.c" */ }; static struct proc_dir_entry proc_ss7_netstat = { PROC_NET_SS7_NETSTAT, 7, "netstat", /* defined in */ S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, netstat_get_info /* defined in "proc.c" */ }; #endif /* #ifdef CONFIG_PROC_FS */ /* * A packet is received from the packet scheduler for the * device. We manage to intercept it here so that we can * send it on to the interface before the hard_start_xmit * to the device driver itself. That way the interface can * process the L2 state machines and queue packets as * desired. Returning a (1) from the function will cause * the packet scheduler to requeue the packet. * * The packet scheduler does not know about our interface. * It gets its information directly from the struct device * owned by the device driver (which we provide to the * device from the interface.) If we need to do any * messing around at the receive side, we can do it here. * * A packet will never come here for the device if it has * it's tbusy flag set, (unless it is coming from a PACKET * socket (?). * */ int ss7_pckt_rcv(struct sk_buff * skb, struct device *dev, struct packet_type* this) { return ss7if_l3_output(skb,dev); /* see ss7_dev.c */ }; static struct packet_type ss7_packet_type = { 0, /* this is htons(ETH_P_SS7) */ 0, /* NULL means for any device */ ss7_pckt_rcv, /* funcion for packet handler to invoke */ NULL, /* packet private data */ NULL /* next pointer, initialize to NULL */ }; #ifdef MODULE void cleanup_module(void) { #ifdef CONFIG_PROC_FS proc_net_unregister(PROC_NET_SS7_MTP); /* defined in */ proc_net_unregister(PROC_NET_SS7_SCCP); /* defined in */ proc_net_unregister(PROC_NET_SS7_SOCKSTAT); /* defined in */ proc_net_unregister(PROC_NET_SS7_NETSTAT); /* defined in */ #endif /* #ifdef CONFIG_PROC_FS */ /* do some groovy cleanup functions, free up stuff * */ #ifdef CONFIG_SYSCTL ss7_unregister_sysctl(); #endif /* #ifdef CONFIG_SYSCTL */ unregister_netdevice_notifier(&ss7_netdevice_notifier); ss7_packet_type.type = htons(ETH_P_SS7); dev_remove_pack(&ss7_packet_type); sock_unregister(ss7_family_ops.family); ss7_rtab_free(); } int init_module(void) #else /* #ifdef MODULE */ __initfunc(void ss7_proto_init(struct net_proto* pro)) #endif /* #ifdef MODULE */ { ss7_rtab_init(); spin_lock_init(&ss7_ifacelist_lock); (void)ss7_ifacelist_lock; sock_register(&ss7_family_ops); ss7_iface_event_init(); ss7_packet_type.type = htons(ETH_P_SS7); dev_add_pack(&ss7_packet_type); register_netdevice_notifier(&ss7_netdev_notifier); #ifdef CONFIG_SYSCTL ss7_register_sysctl(); #endif #ifdef CONFIG_PROC_FS proc_net_register(&proc_ss7); /* register others on their own time! */ #endif printk(KERN_INFO "NET4: SS7 for Linux. $Revision: 0.7.2.5 $ for Linux NET4.0\n"); /* do some groovy initializations here */ #ifdef CONFIG_PROC_FS proc_net_register(&proc_ss7_mtp); /* defined in "proc.c" */ proc_net_register(&proc_ss7_sccp); /* defined in "proc.c" */ proc_net_register(&proc_ss7_netstat); /* defined in "proc.c" */ proc_net_register(&proc_ss7_sockstat); /* defined in "proc.c" */ #endif #ifdef MODULE return 0; #endif } #endif /* defined CONFIG_SS7 */