/***************************************************************************** @(#) $Id: ss7mtp.c,v 0.7.2.1 2000/09/11 07:21:34 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/09/11 07:21:34 $ by $Author: brian $ ----------------------------------------------------------------------------- $Log: ss7mtp.c,v $ 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. *****************************************************************************/ /* * This is the file for the MTP module. This module is responsible for monitoring SS7 * link devices and attaching them to an MTP protocol state machine. The MTP protocol * module is the one interested in receiving device notifications from the links. Not * much can be done in the SS7 stack without an MTP level. The MTP is also responsible * for providing three socket interfaces to the user: SOCK_NTWK_ISEQ, SOCK_NTWK_NSEQ, and * SOCK_RAW. The primary address of the MTP protocol module is the SS7 Point Code (PC). * */ /* * need ss7_mtp_nseq_ops * need ss7_mtp_iseq_ops * need ss7_mtp_raw_ops */ static int ss7mtp_release(struct socket *sock, struct socket *peer); static int ss7mtp_bind(struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len); static int ss7mtp_getname(struct socket *sock, struct sockaddr *uaddr, int *usockaddr_len, int peer); static unsigned int ss7mtp_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait); static int ss7mtp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); static int ss7mtp_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen); static int ss7mtp_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen); static int ss7mtp_sendmsg(struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm); static int ss7mtp_recvmsg(struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm); static struct proto_ops ss7mtp_ops = { PF_SS7, sock_no_dup, ss7mtp_release, ss7mtp_bind, sock_no_connect, sock_no_socketpair, sock_no_accept, ss7mtp_getname, ss7mtp_poll, ss7mtp_ioctl, sock_no_listen, sock_no_shutdown, ss7mtp_setsockopt, ss7mtp_getsockopt, sock_no_fcntl, ss7mtp_sendmsg, ss7mtp_recvmsg }; /* * release = close * * Results from a close I believe... */ static int ss7mtp_release(struct socket *sock, struct socket *peer) { } /* * bind = attach the socket to an address/port * * When an mtp socket binds, it binds to an mtp address (ss7 point code) * and a user part. What we need to do is identify the mtp control block * associated with the ss7 point code and then attach this socket to its * user part. If the user part is already bound, we return an error. If * the ss7 point code is 0.0.0 then we bind to the user part on the * default ss7 address (SS7ADDR_ANY). */ static int ss7mtp_bind(struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len) { } /* * getname = Look up the address of the socket. */ static int ss7mtp_getname(struct socket *sock, struct sockaddr *uaddr, int *usockaddr_len, int peer) { } /* * poll - notify the caller when data is available */ static unsigned int ss7mtp_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait) { } /* * ioctl - perform ioctl on an ss7 socket */ static int ss7mtp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { } /* * setsockopt = sets options at a number of level * * We use this to control some of the options associated with a given mtp * socket. For example, we can make the mtp part not able to transfer * messages or some other things. * */ static __inline int ss7mtp_route_setsockopt(struct sock *sk, int optname, char *optval, int optlen) { return (-ENOPROTOPT); } static __inline int ss7mtp_gws_setsockopt(struct sock *sk, int optname, char *optval, int optlen) { return (-ENOPROTOPT); } static int ss7mtp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) { int val=0,err; /* get values from user */ if (optlen>=sizeof(int)) { if (get_user(val, (int *)optval)) return -EFAULT; } else if (optlen>=sizeof(char)) { unsigned char ucval; if (get_user(ucval, (unsigned char *)opval)) return -EFAULT; val = (int)ucval; } if (level!=SOL_MTP) return -ENOPROTOOPT; /* these are mtp routing options */ if (optname>=SS7RT_OPTS_BASE && optname<=SS7RT_OPTS_BASE+10) { return ss7mtp_route_setsockopt(sk,optname,optval,optlen); } /* these are gateway screening options */ if (optname>=SS7GWS_OPTS_BASE && optname<=SS7GWS_OPTS_BASE+10) { return ss7mtp_gws_setsockopt(sk,optname,optval,optlen); } switch (optname) { /* * This is where we set the options (from val and checking * optlen). There are not options just yet, so we'll deat with * these later. */ default: return (-ENOPROTOPT); } } /* * getsockopt = gets options at a number of levels * * Just the get equivalent of setsockopt. * */ static __inline int ss7mtp_route_getsockopt(struct sock *sk, int optname, char *optval, int *optlen) { return (-ENOPROTOPT); } static __inline int ss7mtp_gws_getsockopt(struct sock *sk, int optname, char *optval, int *optlen) { return (-ENOPROTOPT); } static int ss7mtp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { int val,len; if (level!=SOL_MTP) return -ENOPROTOPT; /* these are mtp routing options */ if (optname>=SS7RT_OPTS_BASE && optname<=SS7RT_OPTS_BASE+10) { return ss7mtp_route_getsockopt(sk,optname,optval,optlen); } /* these are gateway screening options */ if (optname>=SS7GWS_OPTS_BASE && optname<=SS7GWS_OPTS_BASE+10) { return ss7mtp_gws_getsockopt(sk,optname,optval,optlen); } if (get_user(len,optlen)) return -EFAULT; switch (optname) { /* * This is where we get the options (into val and set len). There * are not options just yet, so we'll deal with these later. */ default: return (-ENOPROTOPT); } /* return values to user */ if (len0 && val>=0 && val<255) { unsigned char ucval = (unsigned char)val; len = 1; if (put_user(len,optlen)) return -EFAULT; if (copy_to_user(optval,&ucval,1)) return -EFAULT; } else { len = min(sizeof(int),len); if (put_user(len,optlen)) return -EFAULT; if (copy_to_user(optval,&val,len)) return -EFAULT; } return 0; } /* * sendmsg = sends a message */ static int ss7mtp_sendmsg(struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm) { } /* * recvmsg = receives a message */ static int ss7mtp_recvmsg(struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm) { } static struct proto_ops ss7_ops = { PF_SS7, ss7_dup, ss7_release, ss7_bind, ss7_connect, ss7_socketpair, ss7_accept, ss7_getname, ss7_poll, ss7_ioctl, ss7_listen, ss7_shutdown, ss7_setsockopt, ss7_getsockopt, ss7_fcntl, ss7_sendmsg, ss7_recvmsg }; /* * Someone brought the link into service. We should automatically tell it to align and * prepare to accept traffic. Can't veto this action. */ int ss7_iface_event_up(struct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * Someone brought the link down and out of servier. We should automatically mark the * link out of service and not send it any more traffic. */ int ss7_iface_event_down(struct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * The device has rebooted. We should got through the out-of-service actions (pull * transmission buffers from the interface associated with the device.) And then attempt * to place the link back into service by trying to bring the device up. */ int ss7_iface_event_reboot(struct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * Something about the state of the device changed. */ int ss7_iface_event_change(struct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * The device has registered. That means that a driver has just been loaded and * initialized agains the device. We should go and configure the device and try to bring * it up. */ int ss7_iface_event_register(sturct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * The device has unregistered. It's gone now. Go through the out-of-service cycle for * this device. */ int ss7_iface_event_unregister(sturct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * Devices should not change their name or their MTUs. */ int ss7_iface_event_changemtu(sturct device* device) { (void)device; return NETDEV_BAD; /* for now */ } /* * The device is having its address changed. We must take the device out of service in * its present context and then bring it back into service in the new context. We could * return NOTIFY_BAD if the address is invalid. */ int ss7_iface_event_changeaddr(sturct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * The device is going down, its loosing it. Perform the out-of-service cycle on this * device and its data structures. */ int ss7_iface_event_goingdown(sturct device* device) { (void)device; return NETDEV_DONE; /* for now */ } /* * Devices should not change their name or their MTUs. */ int ss7_iface_event_changename(sturct device* device) { (void)device; return NETDEV_BAD; /* for now */ } static int ss7_iface_event_done(struct device* device) { (void)msg; (void)device; return NETDEV_DONE; } static int ss7_iface_event_bad(struct device* device) { (void)msg; (void)device; return NETDEV_DONE; } static int (*)(struct device*) ss7_iface_event_funcs[16] = { &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done, &ss7_iface_event_done }; static void ss7_iface_event_init(void) { ss7_iface_event_funcs[NETDEV_UP &15] = &ss7_iface_event_up; ss7_iface_event_funcs[NETDEV_DOWN &15] = &ss7_iface_event_down; ss7_iface_event_funcs[NETDEV_REBOOT &15] = &ss7_iface_event_reboot; ss7_iface_event_funcs[NETDEV_CHANGE &15] = &ss7_iface_event_change; ss7_iface_event_funcs[NETDEV_REGISTER &15] = &ss7_iface_event_register; ss7_iface_event_funcs[NETDEV_UNREGISTER&15] = &ss7_iface_event_unregister; ss7_iface_event_funcs[NETDEV_CHANGEMTU &15] = &ss7_iface_event_changemtu; ss7_iface_event_funcs[NETDEV_CHANGEADDR&15] = &ss7_iface_event_changeaddr; ss7_iface_event_funcs[NETDEV_GOING_DOWN&15] = &ss7_iface_event_goingdown; ss7_iface_event_funcs[NETDEV_CHANGENAME&15] = &ss7_iface_event_changename; }; static int ss7_device_event(struct notifier_block *this, unsigned long msg, void *data) { struct device *dev = (struct device *)data; /* * FIrst determine whether we actually have an SS7 device, so we have defined the * dummy ARP Hardware type APRHRD_SS7 in . This brings up an * interesting concept: it could be possible to define an ARP procedures for SS7 over * Ethernet whereby we find the MAC address by broadcasting and resolving on the SS7 * point code. */ if (dev->type != ARPHRD_SS7) return NOTIFY_DONE; if (msg&~15) return NETDEV_DONE; /* * Invoke the appropriate handler. */ return (*ss7_iface_event_funcs[msg&15])(msg,dev); }; static struct notifier_block ss7_netdevice_notifier = { ss7_device_event, NULL, 0 }; #ifdef MODULE void cleanup_module(void) { #ifdef CONFIG_PROC_FS proc_net_unregister(PROC_NET_SS7); #endif /* do some groovy cleanup functions, free up stuff */ #ifdef CONFIG_SYSCTL ss7_unregister_sysctl(); #endif 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); } int init_module(void) #else __initfunc(void ss7_proto_init(struct net_proto* pro)) #endif { 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.1 $ for Linux NET4.0\n"); /* do some groovy initializations here */ #ifdef MODULE return 0; #endif } #endif /* defined CONFIG_SS7 */