2005-04-17 02:20:36 +04:00
/*
* DLCI Implementation of Frame Relay protocol for Linux , according to
* RFC 1490. This generic device provides en / decapsulation for an
* underlying hardware driver . Routes & IPs are assigned to these
* interfaces . Requires ' dlcicfg ' program to create usable
* interfaces , the initial one , ' dlci ' is for IOCTL use only .
*
* Version : @ ( # ) dlci . c 0.35 4 Jan 1997
*
* Author : Mike McLagan < mike . mclagan @ linux . org >
*
* Changes :
*
* 0.15 Mike Mclagan Packet freeing , bug in kmalloc call
* DLCI_RET handling
* 0.20 Mike McLagan More conservative on which packets
* are returned for retry and which are
* are dropped . If DLCI_RET_DROP is
* returned from the FRAD , the packet is
* sent back to Linux for re - transmission
* 0.25 Mike McLagan Converted to use SIOC IOCTL calls
* 0.30 Jim Freeman Fixed to allow IPX traffic
* 0.35 Michael Elizabeth Fixed incorrect memcpy_fromfs
*
* 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 .
*/
2011-06-26 23:01:31 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/interrupt.h>
# include <linux/ptrace.h>
# include <linux/ioport.h>
# include <linux/in.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/if_arp.h>
# include <linux/if_frad.h>
# include <linux/bitops.h>
# include <net/sock.h>
# include <asm/io.h>
# include <asm/dma.h>
# include <asm/uaccess.h>
static const char version [ ] = " DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org " ;
static LIST_HEAD ( dlci_devs ) ;
static void dlci_setup ( struct net_device * ) ;
/*
* these encapsulate the RFC 1490 requirements as well as
* deal with packet transmission and reception , working with
* the upper network layers
*/
static int dlci_header ( struct sk_buff * skb , struct net_device * dev ,
2007-10-09 12:40:57 +04:00
unsigned short type , const void * daddr ,
const void * saddr , unsigned len )
2005-04-17 02:20:36 +04:00
{
struct frhdr hdr ;
unsigned int hlen ;
char * dest ;
hdr . control = FRAD_I_UI ;
2009-11-30 10:42:14 +03:00
switch ( type )
2005-04-17 02:20:36 +04:00
{
case ETH_P_IP :
hdr . IP_NLPID = FRAD_P_IP ;
hlen = sizeof ( hdr . control ) + sizeof ( hdr . IP_NLPID ) ;
break ;
/* feel free to add other types, if necessary */
default :
hdr . pad = FRAD_P_PADDING ;
hdr . NLPID = FRAD_P_SNAP ;
memset ( hdr . OUI , 0 , sizeof ( hdr . OUI ) ) ;
hdr . PID = htons ( type ) ;
hlen = sizeof ( hdr ) ;
break ;
}
dest = skb_push ( skb , hlen ) ;
if ( ! dest )
2010-09-23 09:40:09 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
memcpy ( dest , & hdr , hlen ) ;
2010-09-23 09:40:09 +04:00
return hlen ;
2005-04-17 02:20:36 +04:00
}
static void dlci_receive ( struct sk_buff * skb , struct net_device * dev )
{
struct frhdr * hdr ;
int process , header ;
if ( ! pskb_may_pull ( skb , sizeof ( * hdr ) ) ) {
2011-06-26 23:01:31 +04:00
netdev_notice ( dev , " invalid data no header \n " ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_errors + + ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
return ;
}
hdr = ( struct frhdr * ) skb - > data ;
process = 0 ;
header = 0 ;
skb - > dev = dev ;
if ( hdr - > control ! = FRAD_I_UI )
{
2011-06-26 23:01:31 +04:00
netdev_notice ( dev , " Invalid header flag 0x%02X \n " ,
hdr - > control ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_errors + + ;
2005-04-17 02:20:36 +04:00
}
else
2009-11-30 10:42:14 +03:00
switch ( hdr - > IP_NLPID )
2005-04-17 02:20:36 +04:00
{
case FRAD_P_PADDING :
if ( hdr - > NLPID ! = FRAD_P_SNAP )
{
2011-06-26 23:01:31 +04:00
netdev_notice ( dev , " Unsupported NLPID 0x%02X \n " ,
hdr - > NLPID ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_errors + + ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( hdr - > OUI [ 0 ] + hdr - > OUI [ 1 ] + hdr - > OUI [ 2 ] ! = 0 )
{
2011-06-26 23:01:31 +04:00
netdev_notice ( dev , " Unsupported organizationally unique identifier 0x%02X-%02X-%02X \n " ,
hdr - > OUI [ 0 ] ,
hdr - > OUI [ 1 ] ,
hdr - > OUI [ 2 ] ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_errors + + ;
2005-04-17 02:20:36 +04:00
break ;
}
/* at this point, it's an EtherType frame */
header = sizeof ( struct frhdr ) ;
/* Already in network order ! */
skb - > protocol = hdr - > PID ;
process = 1 ;
break ;
case FRAD_P_IP :
header = sizeof ( hdr - > control ) + sizeof ( hdr - > IP_NLPID ) ;
skb - > protocol = htons ( ETH_P_IP ) ;
process = 1 ;
break ;
case FRAD_P_SNAP :
case FRAD_P_Q933 :
case FRAD_P_CLNP :
2011-06-26 23:01:31 +04:00
netdev_notice ( dev , " Unsupported NLPID 0x%02X \n " ,
hdr - > pad ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_errors + + ;
2005-04-17 02:20:36 +04:00
break ;
default :
2011-06-26 23:01:31 +04:00
netdev_notice ( dev , " Invalid pad byte 0x%02X \n " ,
hdr - > pad ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_errors + + ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( process )
{
/* we've set up the protocol, so discard the header */
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
skb_pull ( skb , header ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_bytes + = skb - > len ;
2005-04-17 02:20:36 +04:00
netif_rx ( skb ) ;
2009-03-20 22:36:14 +03:00
dev - > stats . rx_packets + + ;
2005-04-17 02:20:36 +04:00
}
else
dev_kfree_skb ( skb ) ;
}
2009-09-04 09:33:46 +04:00
static netdev_tx_t dlci_transmit ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2009-09-04 09:33:46 +04:00
struct dlci_local * dlp = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2014-08-30 08:07:24 +04:00
if ( skb ) {
struct netdev_queue * txq = skb_get_tx_queue ( dev , skb ) ;
2014-08-30 08:55:22 +04:00
netdev_start_xmit ( skb , dlp - > slave , txq , false ) ;
2014-08-30 08:07:24 +04:00
}
2009-09-04 09:33:46 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
static int dlci_config ( struct net_device * dev , struct dlci_conf __user * conf , int get )
{
struct dlci_conf config ;
struct dlci_local * dlp ;
struct frad_local * flp ;
int err ;
2008-11-13 10:38:36 +03:00
dlp = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-11-13 10:38:36 +03:00
flp = netdev_priv ( dlp - > slave ) ;
2005-04-17 02:20:36 +04:00
if ( ! get )
{
2009-11-30 10:42:14 +03:00
if ( copy_from_user ( & config , conf , sizeof ( struct dlci_conf ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
if ( config . flags & ~ DLCI_VALID_FLAGS )
2010-09-23 09:40:09 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
memcpy ( & dlp - > config , & config , sizeof ( struct dlci_conf ) ) ;
dlp - > configured = 1 ;
}
err = ( * flp - > dlci_conf ) ( dlp - > slave , dev , get ) ;
if ( err )
2010-09-23 09:40:09 +04:00
return err ;
2005-04-17 02:20:36 +04:00
if ( get )
{
2009-11-30 10:42:14 +03:00
if ( copy_to_user ( conf , & dlp - > config , sizeof ( struct dlci_conf ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
}
2010-09-23 09:40:09 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int dlci_dev_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
struct dlci_local * dlp ;
if ( ! capable ( CAP_NET_ADMIN ) )
2010-09-23 09:40:09 +04:00
return - EPERM ;
2005-04-17 02:20:36 +04:00
2008-11-13 10:38:36 +03:00
dlp = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2009-11-30 10:42:14 +03:00
switch ( cmd )
2005-04-17 02:20:36 +04:00
{
case DLCI_GET_SLAVE :
if ( ! * ( short * ) ( dev - > dev_addr ) )
2010-09-23 09:40:09 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
strncpy ( ifr - > ifr_slave , dlp - > slave - > name , sizeof ( ifr - > ifr_slave ) ) ;
break ;
case DLCI_GET_CONF :
case DLCI_SET_CONF :
if ( ! * ( short * ) ( dev - > dev_addr ) )
2010-09-23 09:40:09 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-09-23 09:40:09 +04:00
return dlci_config ( dev , ifr - > ifr_data , cmd = = DLCI_GET_CONF ) ;
2005-04-17 02:20:36 +04:00
default :
2010-09-23 09:40:09 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
2010-09-23 09:40:09 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int dlci_change_mtu ( struct net_device * dev , int new_mtu )
{
2009-03-20 22:36:15 +03:00
struct dlci_local * dlp = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2009-03-20 22:36:15 +03:00
return dev_set_mtu ( dlp - > slave , new_mtu ) ;
2005-04-17 02:20:36 +04:00
}
static int dlci_open ( struct net_device * dev )
{
struct dlci_local * dlp ;
struct frad_local * flp ;
int err ;
2008-11-13 10:38:36 +03:00
dlp = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( ! * ( short * ) ( dev - > dev_addr ) )
2010-09-23 09:40:09 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( ! netif_running ( dlp - > slave ) )
2010-09-23 09:40:09 +04:00
return - ENOTCONN ;
2005-04-17 02:20:36 +04:00
2008-11-13 10:38:36 +03:00
flp = netdev_priv ( dlp - > slave ) ;
2005-04-17 02:20:36 +04:00
err = ( * flp - > activate ) ( dlp - > slave , dev ) ;
if ( err )
2010-09-23 09:40:09 +04:00
return err ;
2005-04-17 02:20:36 +04:00
netif_start_queue ( dev ) ;
return 0 ;
}
static int dlci_close ( struct net_device * dev )
{
struct dlci_local * dlp ;
struct frad_local * flp ;
int err ;
netif_stop_queue ( dev ) ;
2008-11-13 10:38:36 +03:00
dlp = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-11-13 10:38:36 +03:00
flp = netdev_priv ( dlp - > slave ) ;
2005-04-17 02:20:36 +04:00
err = ( * flp - > deactivate ) ( dlp - > slave , dev ) ;
return 0 ;
}
static int dlci_add ( struct dlci_add * dlci )
{
struct net_device * master , * slave ;
struct dlci_local * dlp ;
struct frad_local * flp ;
int err = - EINVAL ;
/* validate slave device */
2007-09-17 22:56:21 +04:00
slave = dev_get_by_name ( & init_net , dlci - > devname ) ;
2005-04-17 02:20:36 +04:00
if ( ! slave )
return - ENODEV ;
2008-11-13 10:38:36 +03:00
if ( slave - > type ! = ARPHRD_FRAD | | netdev_priv ( slave ) = = NULL )
2005-04-17 02:20:36 +04:00
goto err1 ;
/* create device name */
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 18:37:24 +04:00
master = alloc_netdev ( sizeof ( struct dlci_local ) , " dlci%d " ,
NET_NAME_UNKNOWN , dlci_setup ) ;
2005-04-17 02:20:36 +04:00
if ( ! master ) {
err = - ENOMEM ;
goto err1 ;
}
/* make sure same slave not already registered */
rtnl_lock ( ) ;
list_for_each_entry ( dlp , & dlci_devs , list ) {
if ( dlp - > slave = = slave ) {
err = - EBUSY ;
goto err2 ;
}
}
* ( short * ) ( master - > dev_addr ) = dlci - > dlci ;
2008-11-13 10:38:36 +03:00
dlp = netdev_priv ( master ) ;
2005-04-17 02:20:36 +04:00
dlp - > slave = slave ;
dlp - > master = master ;
2008-11-13 10:38:36 +03:00
flp = netdev_priv ( slave ) ;
2005-04-17 02:20:36 +04:00
err = ( * flp - > assoc ) ( slave , master ) ;
if ( err < 0 )
goto err2 ;
err = register_netdevice ( master ) ;
if ( err < 0 )
goto err2 ;
strcpy ( dlci - > devname , master - > name ) ;
list_add ( & dlp - > list , & dlci_devs ) ;
rtnl_unlock ( ) ;
2010-09-23 09:40:09 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
err2 :
rtnl_unlock ( ) ;
free_netdev ( master ) ;
err1 :
dev_put ( slave ) ;
2010-09-23 09:40:09 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static int dlci_del ( struct dlci_add * dlci )
{
struct dlci_local * dlp ;
struct frad_local * flp ;
struct net_device * master , * slave ;
int err ;
2013-06-26 11:31:58 +04:00
bool found = false ;
2005-04-17 02:20:36 +04:00
2013-06-26 11:29:54 +04:00
rtnl_lock ( ) ;
2005-04-17 02:20:36 +04:00
/* validate slave device */
2007-09-17 22:56:21 +04:00
master = __dev_get_by_name ( & init_net , dlci - > devname ) ;
2013-06-26 11:29:54 +04:00
if ( ! master ) {
err = - ENODEV ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2013-06-26 11:31:58 +04:00
list_for_each_entry ( dlp , & dlci_devs , list ) {
if ( dlp - > master = = master ) {
found = true ;
break ;
}
}
if ( ! found ) {
err = - ENODEV ;
goto out ;
}
2005-04-17 02:20:36 +04:00
if ( netif_running ( master ) ) {
2013-06-26 11:29:54 +04:00
err = - EBUSY ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-11-13 10:38:36 +03:00
dlp = netdev_priv ( master ) ;
2005-04-17 02:20:36 +04:00
slave = dlp - > slave ;
2008-11-13 10:38:36 +03:00
flp = netdev_priv ( slave ) ;
2005-04-17 02:20:36 +04:00
err = ( * flp - > deassoc ) ( slave , master ) ;
if ( ! err ) {
list_del ( & dlp - > list ) ;
unregister_netdevice ( master ) ;
dev_put ( slave ) ;
}
2013-06-26 11:29:54 +04:00
out :
2005-04-17 02:20:36 +04:00
rtnl_unlock ( ) ;
2010-09-23 09:40:09 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static int dlci_ioctl ( unsigned int cmd , void __user * arg )
{
struct dlci_add add ;
int err ;
if ( ! capable ( CAP_NET_ADMIN ) )
2010-09-23 09:40:09 +04:00
return - EPERM ;
2005-04-17 02:20:36 +04:00
2009-11-30 10:42:14 +03:00
if ( copy_from_user ( & add , arg , sizeof ( struct dlci_add ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
switch ( cmd )
{
case SIOCADDDLCI :
err = dlci_add ( & add ) ;
if ( ! err )
2009-11-30 10:42:14 +03:00
if ( copy_to_user ( arg , & add , sizeof ( struct dlci_add ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
break ;
case SIOCDELDLCI :
err = dlci_del ( & add ) ;
break ;
default :
err = - EINVAL ;
}
2010-09-23 09:40:09 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-10-09 12:40:57 +04:00
static const struct header_ops dlci_header_ops = {
. create = dlci_header ,
} ;
2009-03-20 22:36:15 +03:00
static const struct net_device_ops dlci_netdev_ops = {
. ndo_open = dlci_open ,
. ndo_stop = dlci_close ,
. ndo_do_ioctl = dlci_dev_ioctl ,
. ndo_start_xmit = dlci_transmit ,
. ndo_change_mtu = dlci_change_mtu ,
} ;
2005-04-17 02:20:36 +04:00
static void dlci_setup ( struct net_device * dev )
{
2008-11-13 10:38:36 +03:00
struct dlci_local * dlp = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
dev - > flags = 0 ;
2007-10-09 12:40:57 +04:00
dev - > header_ops = & dlci_header_ops ;
2009-03-20 22:36:15 +03:00
dev - > netdev_ops = & dlci_netdev_ops ;
2005-04-17 02:20:36 +04:00
dev - > destructor = free_netdev ;
dlp - > receive = dlci_receive ;
dev - > type = ARPHRD_DLCI ;
dev - > hard_header_len = sizeof ( struct frhdr ) ;
dev - > addr_len = sizeof ( short ) ;
}
/* if slave is unregistering, then cleanup master */
static int dlci_dev_event ( struct notifier_block * unused ,
unsigned long event , void * ptr )
{
2013-05-28 05:30:21 +04:00
struct net_device * dev = netdev_notifier_info_to_dev ( ptr ) ;
2005-04-17 02:20:36 +04:00
2008-03-25 15:47:49 +03:00
if ( dev_net ( dev ) ! = & init_net )
2007-09-12 15:02:17 +04:00
return NOTIFY_DONE ;
2005-04-17 02:20:36 +04:00
if ( event = = NETDEV_UNREGISTER ) {
struct dlci_local * dlp ;
list_for_each_entry ( dlp , & dlci_devs , list ) {
if ( dlp - > slave = = dev ) {
list_del ( & dlp - > list ) ;
unregister_netdevice ( dlp - > master ) ;
dev_put ( dlp - > slave ) ;
break ;
}
}
}
return NOTIFY_DONE ;
}
static struct notifier_block dlci_notifier = {
. notifier_call = dlci_dev_event ,
} ;
static int __init init_dlci ( void )
{
dlci_ioctl_set ( dlci_ioctl ) ;
register_netdevice_notifier ( & dlci_notifier ) ;
printk ( " %s. \n " , version ) ;
return 0 ;
}
static void __exit dlci_exit ( void )
{
struct dlci_local * dlp , * nxt ;
dlci_ioctl_set ( NULL ) ;
unregister_netdevice_notifier ( & dlci_notifier ) ;
rtnl_lock ( ) ;
list_for_each_entry_safe ( dlp , nxt , & dlci_devs , list ) {
unregister_netdevice ( dlp - > master ) ;
dev_put ( dlp - > slave ) ;
}
rtnl_unlock ( ) ;
}
module_init ( init_dlci ) ;
module_exit ( dlci_exit ) ;
MODULE_AUTHOR ( " Mike McLagan " ) ;
MODULE_DESCRIPTION ( " Frame Relay DLCI layer " ) ;
MODULE_LICENSE ( " GPL " ) ;