2005-04-17 02:20:36 +04:00
/*
* Generic HDLC support routines for Linux
* Cisco HDLC support
*
2006-09-27 01:23:45 +04:00
* Copyright ( C ) 2000 - 2006 Krzysztof Halasa < khc @ pm . waw . pl >
2005-04-17 02:20:36 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation .
*/
# include <linux/errno.h>
2008-06-30 21:06:40 +04:00
# include <linux/hdlc.h>
2005-04-17 02:20:36 +04:00
# include <linux/if_arp.h>
2008-06-30 21:06:40 +04:00
# include <linux/inetdevice.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
2008-06-30 21:06:40 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/pkt_sched.h>
2008-06-30 21:06:40 +04:00
# include <linux/poll.h>
2005-04-17 02:20:36 +04:00
# include <linux/rtnetlink.h>
2008-06-30 21:06:40 +04:00
# include <linux/skbuff.h>
2005-04-17 02:20:36 +04:00
# undef DEBUG_HARD_HEADER
# define CISCO_MULTICAST 0x8F /* Cisco multicast address */
# define CISCO_UNICAST 0x0F /* Cisco unicast address */
# define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
# define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */
# define CISCO_ADDR_REQ 0 /* Cisco address request */
# define CISCO_ADDR_REPLY 1 /* Cisco address reply */
# define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
2006-09-27 01:23:45 +04:00
struct hdlc_header {
u8 address ;
u8 control ;
2007-04-27 15:13:33 +04:00
__be16 protocol ;
2010-06-02 22:10:09 +04:00
} __packed ;
2006-09-27 01:23:45 +04:00
struct cisco_packet {
2007-04-27 15:13:33 +04:00
__be32 type ; /* code */
__be32 par1 ;
__be32 par2 ;
__be16 rel ; /* reliability */
__be32 time ;
2010-06-02 22:10:09 +04:00
} __packed ;
2006-09-27 01:23:45 +04:00
# define CISCO_PACKET_LEN 18
# define CISCO_BIG_PACKET_LEN 20
struct cisco_state {
cisco_proto settings ;
struct timer_list timer ;
2008-05-19 21:11:08 +04:00
spinlock_t lock ;
2006-09-27 01:23:45 +04:00
unsigned long last_poll ;
int up ;
2009-10-09 10:16:10 +04:00
u32 txseq ; /* TX sequence number, 0 = none */
2006-09-27 01:23:45 +04:00
u32 rxseq ; /* RX sequence number */
} ;
static int cisco_ioctl ( struct net_device * dev , struct ifreq * ifr ) ;
2008-06-30 21:06:40 +04:00
static inline struct cisco_state * state ( hdlc_device * hdlc )
2006-09-27 01:23:45 +04:00
{
2008-06-30 21:06:40 +04:00
return ( struct cisco_state * ) hdlc - > state ;
2006-09-27 01:23:45 +04:00
}
2005-04-17 02:20:36 +04:00
static int cisco_hard_header ( struct sk_buff * skb , struct net_device * dev ,
2007-10-09 12:40:57 +04:00
u16 type , const void * daddr , const void * saddr ,
2005-04-17 02:20:36 +04:00
unsigned int len )
{
2006-09-27 01:23:45 +04:00
struct hdlc_header * data ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG_HARD_HEADER
printk ( KERN_DEBUG " %s: cisco_hard_header called \n " , dev - > name ) ;
# endif
2006-09-27 01:23:45 +04:00
skb_push ( skb , sizeof ( struct hdlc_header ) ) ;
data = ( struct hdlc_header * ) skb - > data ;
2005-04-17 02:20:36 +04:00
if ( type = = CISCO_KEEPALIVE )
data - > address = CISCO_MULTICAST ;
else
data - > address = CISCO_UNICAST ;
data - > control = 0 ;
data - > protocol = htons ( type ) ;
2006-09-27 01:23:45 +04:00
return sizeof ( struct hdlc_header ) ;
2005-04-17 02:20:36 +04:00
}
static void cisco_keepalive_send ( struct net_device * dev , u32 type ,
2007-04-27 15:13:33 +04:00
__be32 par1 , __be32 par2 )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
2006-09-27 01:23:45 +04:00
struct cisco_packet * data ;
2005-04-17 02:20:36 +04:00
2006-09-27 01:23:45 +04:00
skb = dev_alloc_skb ( sizeof ( struct hdlc_header ) +
sizeof ( struct cisco_packet ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! skb ) {
printk ( KERN_WARNING
" %s: Memory squeeze on cisco_keepalive_send() \n " ,
dev - > name ) ;
return ;
}
skb_reserve ( skb , 4 ) ;
cisco_hard_header ( skb , dev , CISCO_KEEPALIVE , NULL , NULL , 0 ) ;
2006-09-27 01:23:45 +04:00
data = ( struct cisco_packet * ) ( skb - > data + 4 ) ;
2005-04-17 02:20:36 +04:00
data - > type = htonl ( type ) ;
2007-04-27 15:13:33 +04:00
data - > par1 = par1 ;
data - > par2 = par2 ;
2009-02-01 11:45:17 +03:00
data - > rel = cpu_to_be16 ( 0xFFFF ) ;
2005-04-17 02:20:36 +04:00
/* we will need do_div here if 1000 % HZ != 0 */
data - > time = htonl ( ( jiffies - INITIAL_JIFFIES ) * ( 1000 / HZ ) ) ;
2006-09-27 01:23:45 +04:00
skb_put ( skb , sizeof ( struct cisco_packet ) ) ;
2005-04-17 02:20:36 +04:00
skb - > priority = TC_PRIO_CONTROL ;
skb - > dev = dev ;
2007-04-11 07:45:18 +04:00
skb_reset_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
dev_queue_xmit ( skb ) ;
}
2005-07-12 23:08:43 +04:00
static __be16 cisco_type_trans ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2006-09-27 01:23:45 +04:00
struct hdlc_header * data = ( struct hdlc_header * ) skb - > data ;
2005-04-17 02:20:36 +04:00
2006-09-27 01:23:45 +04:00
if ( skb - > len < sizeof ( struct hdlc_header ) )
2009-02-01 11:45:17 +03:00
return cpu_to_be16 ( ETH_P_HDLC ) ;
2005-04-17 02:20:36 +04:00
if ( data - > address ! = CISCO_MULTICAST & &
data - > address ! = CISCO_UNICAST )
2009-02-01 11:45:17 +03:00
return cpu_to_be16 ( ETH_P_HDLC ) ;
2005-04-17 02:20:36 +04:00
2009-12-09 17:35:40 +03:00
switch ( data - > protocol ) {
2009-02-01 11:45:17 +03:00
case cpu_to_be16 ( ETH_P_IP ) :
case cpu_to_be16 ( ETH_P_IPX ) :
case cpu_to_be16 ( ETH_P_IPV6 ) :
2006-09-27 01:23:45 +04:00
skb_pull ( skb , sizeof ( struct hdlc_header ) ) ;
2005-04-17 02:20:36 +04:00
return data - > protocol ;
default :
2009-02-01 11:45:17 +03:00
return cpu_to_be16 ( ETH_P_HDLC ) ;
2005-04-17 02:20:36 +04:00
}
}
static int cisco_rx ( struct sk_buff * skb )
{
struct net_device * dev = skb - > dev ;
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
2008-05-19 21:11:08 +04:00
struct cisco_state * st = state ( hdlc ) ;
2006-09-27 01:23:45 +04:00
struct hdlc_header * data = ( struct hdlc_header * ) skb - > data ;
struct cisco_packet * cisco_data ;
2005-04-17 02:20:36 +04:00
struct in_device * in_dev ;
2006-09-29 05:00:55 +04:00
__be32 addr , mask ;
2009-10-09 10:16:10 +04:00
u32 ack ;
2005-04-17 02:20:36 +04:00
2006-09-27 01:23:45 +04:00
if ( skb - > len < sizeof ( struct hdlc_header ) )
2005-04-17 02:20:36 +04:00
goto rx_error ;
if ( data - > address ! = CISCO_MULTICAST & &
data - > address ! = CISCO_UNICAST )
goto rx_error ;
2008-06-30 21:06:40 +04:00
switch ( ntohs ( data - > protocol ) ) {
2005-04-17 02:20:36 +04:00
case CISCO_SYS_INFO :
/* Packet is not needed, drop it. */
dev_kfree_skb_any ( skb ) ;
return NET_RX_SUCCESS ;
case CISCO_KEEPALIVE :
2006-09-27 01:23:45 +04:00
if ( ( skb - > len ! = sizeof ( struct hdlc_header ) +
CISCO_PACKET_LEN ) & &
( skb - > len ! = sizeof ( struct hdlc_header ) +
CISCO_BIG_PACKET_LEN ) ) {
printk ( KERN_INFO " %s: Invalid length of Cisco control "
" packet (%d bytes) \n " , dev - > name , skb - > len ) ;
2005-04-17 02:20:36 +04:00
goto rx_error ;
}
2006-09-27 01:23:45 +04:00
cisco_data = ( struct cisco_packet * ) ( skb - > data + sizeof
( struct hdlc_header ) ) ;
2005-04-17 02:20:36 +04:00
2009-12-09 17:35:40 +03:00
switch ( ntohl ( cisco_data - > type ) ) {
2005-04-17 02:20:36 +04:00
case CISCO_ADDR_REQ : /* Stolen from syncppp.c :-) */
2010-09-15 08:04:31 +04:00
rcu_read_lock ( ) ;
in_dev = __in_dev_get_rcu ( dev ) ;
2005-04-17 02:20:36 +04:00
addr = 0 ;
2009-02-01 11:45:17 +03:00
mask = ~ cpu_to_be32 ( 0 ) ; /* is the mask correct? */
2005-04-17 02:20:36 +04:00
if ( in_dev ! = NULL ) {
struct in_ifaddr * * ifap = & in_dev - > ifa_list ;
while ( * ifap ! = NULL ) {
if ( strcmp ( dev - > name ,
( * ifap ) - > ifa_label ) = = 0 ) {
addr = ( * ifap ) - > ifa_local ;
mask = ( * ifap ) - > ifa_mask ;
break ;
}
ifap = & ( * ifap ) - > ifa_next ;
}
cisco_keepalive_send ( dev , CISCO_ADDR_REPLY ,
addr , mask ) ;
}
2010-09-15 08:04:31 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
return NET_RX_SUCCESS ;
case CISCO_ADDR_REPLY :
printk ( KERN_INFO " %s: Unexpected Cisco IP address "
" reply \n " , dev - > name ) ;
goto rx_error ;
case CISCO_KEEPALIVE_REQ :
2008-05-19 21:11:08 +04:00
spin_lock ( & st - > lock ) ;
st - > rxseq = ntohl ( cisco_data - > par1 ) ;
2009-10-09 10:16:10 +04:00
ack = ntohl ( cisco_data - > par2 ) ;
if ( ack & & ( ack = = st - > txseq | |
/* our current REQ may be in transit */
ack = = st - > txseq - 1 ) ) {
2008-05-19 21:11:08 +04:00
st - > last_poll = jiffies ;
if ( ! st - > up ) {
2005-04-17 02:20:36 +04:00
u32 sec , min , hrs , days ;
sec = ntohl ( cisco_data - > time ) / 1000 ;
min = sec / 60 ; sec - = min * 60 ;
hrs = min / 60 ; min - = hrs * 60 ;
days = hrs / 24 ; hrs - = days * 24 ;
printk ( KERN_INFO " %s: Link up (peer "
" uptime %ud%uh%um%us) \n " ,
2008-05-19 21:11:08 +04:00
dev - > name , days , hrs , min , sec ) ;
2006-07-13 00:46:12 +04:00
netif_dormant_off ( dev ) ;
2008-05-19 21:11:08 +04:00
st - > up = 1 ;
2005-04-17 02:20:36 +04:00
}
}
2008-05-19 21:11:08 +04:00
spin_unlock ( & st - > lock ) ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
return NET_RX_SUCCESS ;
2009-12-09 17:35:40 +03:00
} /* switch (keepalive type) */
} /* switch (protocol) */
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " %s: Unsupported protocol %x \n " , dev - > name ,
2007-04-27 15:13:33 +04:00
ntohs ( data - > protocol ) ) ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
return NET_RX_DROP ;
2008-07-01 01:26:53 +04:00
rx_error :
dev - > stats . rx_errors + + ; /* Mark error */
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
return NET_RX_DROP ;
}
static void cisco_timer ( unsigned long arg )
{
struct net_device * dev = ( struct net_device * ) arg ;
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
2008-05-19 21:11:08 +04:00
struct cisco_state * st = state ( hdlc ) ;
2005-04-17 02:20:36 +04:00
2008-05-19 21:11:08 +04:00
spin_lock ( & st - > lock ) ;
if ( st - > up & &
time_after ( jiffies , st - > last_poll + st - > settings . timeout * HZ ) ) {
st - > up = 0 ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " %s: Link down \n " , dev - > name ) ;
2006-07-13 00:46:12 +04:00
netif_dormant_on ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-19 21:11:08 +04:00
cisco_keepalive_send ( dev , CISCO_KEEPALIVE_REQ , htonl ( + + st - > txseq ) ,
htonl ( st - > rxseq ) ) ;
spin_unlock ( & st - > lock ) ;
st - > timer . expires = jiffies + st - > settings . interval * HZ ;
st - > timer . function = cisco_timer ;
st - > timer . data = arg ;
add_timer ( & st - > timer ) ;
2005-04-17 02:20:36 +04:00
}
static void cisco_start ( struct net_device * dev )
{
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
2008-05-19 21:11:08 +04:00
struct cisco_state * st = state ( hdlc ) ;
unsigned long flags ;
spin_lock_irqsave ( & st - > lock , flags ) ;
2009-10-09 10:16:10 +04:00
st - > up = st - > txseq = st - > rxseq = 0 ;
2008-05-19 21:11:08 +04:00
spin_unlock_irqrestore ( & st - > lock , flags ) ;
init_timer ( & st - > timer ) ;
st - > timer . expires = jiffies + HZ ; /* First poll after 1 s */
st - > timer . function = cisco_timer ;
st - > timer . data = ( unsigned long ) dev ;
add_timer ( & st - > timer ) ;
2005-04-17 02:20:36 +04:00
}
static void cisco_stop ( struct net_device * dev )
{
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
2008-05-19 21:11:08 +04:00
struct cisco_state * st = state ( hdlc ) ;
unsigned long flags ;
del_timer_sync ( & st - > timer ) ;
spin_lock_irqsave ( & st - > lock , flags ) ;
2006-07-13 00:46:12 +04:00
netif_dormant_on ( dev ) ;
2009-10-09 10:16:10 +04:00
st - > up = st - > txseq = 0 ;
2008-05-19 21:11:08 +04:00
spin_unlock_irqrestore ( & st - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-27 01:23:45 +04:00
static struct hdlc_proto proto = {
. start = cisco_start ,
. stop = cisco_stop ,
. type_trans = cisco_type_trans ,
. ioctl = cisco_ioctl ,
2008-02-02 00:37:12 +03:00
. netif_rx = cisco_rx ,
2006-09-27 01:23:45 +04:00
. module = THIS_MODULE ,
} ;
2007-10-09 12:40:57 +04:00
static const struct header_ops cisco_header_ops = {
. create = cisco_hard_header ,
} ;
2008-06-30 21:06:40 +04:00
2006-09-27 01:23:45 +04:00
static int cisco_ioctl ( struct net_device * dev , struct ifreq * ifr )
2005-04-17 02:20:36 +04:00
{
cisco_proto __user * cisco_s = ifr - > ifr_settings . ifs_ifsu . cisco ;
const size_t size = sizeof ( cisco_proto ) ;
cisco_proto new_settings ;
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
int result ;
switch ( ifr - > ifr_settings . type ) {
case IF_GET_PROTO :
2006-09-27 01:23:45 +04:00
if ( dev_to_hdlc ( dev ) - > proto ! = & proto )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
ifr - > ifr_settings . type = IF_PROTO_CISCO ;
if ( ifr - > ifr_settings . size < size ) {
ifr - > ifr_settings . size = size ; /* data size wanted */
return - ENOBUFS ;
}
2006-09-27 01:23:45 +04:00
if ( copy_to_user ( cisco_s , & state ( hdlc ) - > settings , size ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
case IF_PROTO_CISCO :
2008-06-30 21:06:40 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
2005-04-17 02:20:36 +04:00
return - EPERM ;
2008-06-30 21:06:40 +04:00
if ( dev - > flags & IFF_UP )
2005-04-17 02:20:36 +04:00
return - EBUSY ;
if ( copy_from_user ( & new_settings , cisco_s , size ) )
return - EFAULT ;
if ( new_settings . interval < 1 | |
new_settings . timeout < 2 )
return - EINVAL ;
2008-06-30 21:06:40 +04:00
result = hdlc - > attach ( dev , ENCODING_NRZ , PARITY_CRC16_PR1_CCITT ) ;
2005-04-17 02:20:36 +04:00
if ( result )
return result ;
2008-02-02 00:37:12 +03:00
result = attach_hdlc_protocol ( dev , & proto ,
2006-09-27 01:23:45 +04:00
sizeof ( struct cisco_state ) ) ;
if ( result )
return result ;
2005-04-17 02:20:36 +04:00
2006-09-27 01:23:45 +04:00
memcpy ( & state ( hdlc ) - > settings , & new_settings , size ) ;
2008-05-19 21:11:08 +04:00
spin_lock_init ( & state ( hdlc ) - > lock ) ;
2007-10-09 12:40:57 +04:00
dev - > header_ops = & cisco_header_ops ;
2005-04-17 02:20:36 +04:00
dev - > type = ARPHRD_CISCO ;
2006-07-13 00:46:12 +04:00
netif_dormant_on ( dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
return - EINVAL ;
}
2006-09-27 01:23:45 +04:00
static int __init mod_init ( void )
{
register_hdlc_protocol ( & proto ) ;
return 0 ;
}
static void __exit mod_exit ( void )
{
unregister_hdlc_protocol ( & proto ) ;
}
module_init ( mod_init ) ;
module_exit ( mod_exit ) ;
MODULE_AUTHOR ( " Krzysztof Halasa <khc@pm.waw.pl> " ) ;
MODULE_DESCRIPTION ( " Cisco HDLC protocol support for generic HDLC " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;