2019-05-28 20:10:09 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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
*/
# 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 ;
2017-10-17 03:29:22 +03:00
struct net_device * dev ;
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
2020-01-07 12:33:46 +03:00
netdev_dbg ( dev , " %s called \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
# 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 ) {
2020-01-07 12:33:46 +03:00
netdev_warn ( dev , " Memory squeeze on %s() \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
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 ;
drivers/net/wan/hdlc: Set skb->protocol before transmitting
This patch sets skb->protocol before transmitting frames on the HDLC
device, so that a user listening on the HDLC device with an AF_PACKET
socket will see outgoing frames' sll_protocol field correctly set and
consistent with that of incoming frames.
1. Control frames in hdlc_cisco and hdlc_ppp
When these drivers send control frames, skb->protocol is not set.
This value should be set to htons(ETH_P_HDLC), because when receiving
control frames, their skb->protocol is set to htons(ETH_P_HDLC).
When receiving, hdlc_type_trans in hdlc.h is called, which then calls
cisco_type_trans or ppp_type_trans. The skb->protocol of control frames
is set to htons(ETH_P_HDLC) so that the control frames can be received
by hdlc_rcv in hdlc.c, which calls cisco_rx or ppp_rx to process the
control frames.
2. hdlc_fr
When this driver sends control frames, skb->protocol is set to internal
values used in this driver.
When this driver sends data frames (from upper stacked PVC devices),
skb->protocol is the same as that of the user data packet being sent on
the upper PVC device (for normal PVC devices), or is htons(ETH_P_802_3)
(for Ethernet-emulating PVC devices).
However, skb->protocol for both control frames and data frames should be
set to htons(ETH_P_HDLC), because when receiving, all frames received on
the HDLC device will have their skb->protocol set to htons(ETH_P_HDLC).
When receiving, hdlc_type_trans in hdlc.h is called, and because this
driver doesn't provide a type_trans function in struct hdlc_proto,
all frames will have their skb->protocol set to htons(ETH_P_HDLC).
The frames are then received by hdlc_rcv in hdlc.c, which calls fr_rx
to process the frames (control frames are consumed and data frames
are re-received on upper PVC devices).
Cc: Krzysztof Halasa <khc@pm.waw.pl>
Signed-off-by: Xie He <xie.he.0141@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-17 00:25:07 +03:00
skb - > protocol = htons ( ETH_P_HDLC ) ;
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 ) ) {
2011-06-26 23:01:28 +04:00
netdev_info ( dev , " Invalid length of Cisco control packet (%d bytes) \n " ,
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 ) {
2019-05-31 19:27:08 +03:00
const struct in_ifaddr * ifa ;
2005-04-17 02:20:36 +04:00
2019-05-31 19:27:08 +03:00
in_dev_for_each_ifa_rcu ( ifa , in_dev ) {
2005-04-17 02:20:36 +04:00
if ( strcmp ( dev - > name ,
2019-05-31 19:27:08 +03:00
ifa - > ifa_label ) = = 0 ) {
addr = ifa - > ifa_local ;
mask = ifa - > ifa_mask ;
2005-04-17 02:20:36 +04:00
break ;
}
}
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 :
2011-06-26 23:01:28 +04:00
netdev_info ( dev , " Unexpected Cisco IP address reply \n " ) ;
2005-04-17 02:20:36 +04:00
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 ;
2011-06-26 23:01:28 +04:00
netdev_info ( dev , " Link up (peer uptime %ud%uh%um%us) \n " ,
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
2011-06-26 23:01:28 +04:00
netdev_info ( dev , " Unsupported protocol %x \n " , 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 ;
}
2017-10-17 03:29:22 +03:00
static void cisco_timer ( struct timer_list * t )
2005-04-17 02:20:36 +04:00
{
2017-10-17 03:29:22 +03:00
struct cisco_state * st = from_timer ( st , t , timer ) ;
struct net_device * dev = st - > dev ;
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 ;
2011-06-26 23:01:28 +04:00
netdev_info ( dev , " Link down \n " ) ;
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 ;
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 ) ;
2017-10-17 03:29:22 +03:00
st - > dev = dev ;
timer_setup ( & st - > timer , cisco_timer , 0 ) ;
2008-05-19 21:11:08 +04:00
st - > timer . expires = jiffies + HZ ; /* First poll after 1 s */
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 ;
2020-08-28 10:07:52 +03:00
dev - > hard_header_len = sizeof ( struct hdlc_header ) ;
2005-04-17 02:20:36 +04:00
dev - > type = ARPHRD_CISCO ;
2015-12-03 23:12:31 +03:00
call_netdevice_notifiers ( NETDEV_POST_TYPE_CHANGE , dev ) ;
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 " ) ;