2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/timer.h>
# include <linux/init.h>
# include <linux/bitops.h>
2006-01-11 12:17:47 -08:00
# include <linux/capability.h>
2005-04-16 15:20:36 -07:00
# include <linux/seq_file.h>
/* We are an ethernet device */
# include <linux/if_ether.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <net/sock.h>
# include <linux/skbuff.h>
# include <linux/ip.h>
# include <asm/byteorder.h>
# include <asm/uaccess.h>
# include <net/checksum.h> /* for ip_fast_csum() */
# include <net/arp.h>
# include <net/dst.h>
# include <linux/proc_fs.h>
/* And atm device */
# include <linux/atmdev.h>
# include <linux/atmlec.h>
# include <linux/atmmpc.h>
/* Modular too */
# include <linux/module.h>
# include "lec.h"
# include "mpc.h"
# include "resources.h"
/*
2007-02-09 23:24:29 +09:00
* mpc . c : Implementation of MPOA client kernel part
2005-04-16 15:20:36 -07:00
*/
#if 0
# define dprintk printk /* debug */
# else
# define dprintk(format,args...)
# endif
#if 0
# define ddprintk printk /* more debug */
# else
# define ddprintk(format,args...)
# endif
# define MPOA_TAG_LEN 4
/* mpc_daemon -> kernel */
static void MPOA_trigger_rcvd ( struct k_message * msg , struct mpoa_client * mpc ) ;
static void MPOA_res_reply_rcvd ( struct k_message * msg , struct mpoa_client * mpc ) ;
static void ingress_purge_rcvd ( struct k_message * msg , struct mpoa_client * mpc ) ;
static void egress_purge_rcvd ( struct k_message * msg , struct mpoa_client * mpc ) ;
static void mps_death ( struct k_message * msg , struct mpoa_client * mpc ) ;
static void clean_up ( struct k_message * msg , struct mpoa_client * mpc , int action ) ;
static void MPOA_cache_impos_rcvd ( struct k_message * msg , struct mpoa_client * mpc ) ;
static void set_mpc_ctrl_addr_rcvd ( struct k_message * mesg , struct mpoa_client * mpc ) ;
static void set_mps_mac_addr_rcvd ( struct k_message * mesg , struct mpoa_client * mpc ) ;
2008-07-30 16:31:46 -07:00
static const uint8_t * copy_macs ( struct mpoa_client * mpc ,
const uint8_t * router_mac ,
const uint8_t * tlvs , uint8_t mps_macs ,
uint8_t device_type ) ;
2005-04-16 15:20:36 -07:00
static void purge_egress_shortcut ( struct atm_vcc * vcc , eg_cache_entry * entry ) ;
2008-07-30 16:31:46 -07:00
static void send_set_mps_ctrl_addr ( const char * addr , struct mpoa_client * mpc ) ;
2005-04-16 15:20:36 -07:00
static void mpoad_close ( struct atm_vcc * vcc ) ;
static int msg_from_mpoad ( struct atm_vcc * vcc , struct sk_buff * skb ) ;
static void mpc_push ( struct atm_vcc * vcc , struct sk_buff * skb ) ;
static int mpc_send_packet ( struct sk_buff * skb , struct net_device * dev ) ;
static int mpoa_event_listener ( struct notifier_block * mpoa_notifier , unsigned long event , void * dev ) ;
static void mpc_timer_refresh ( void ) ;
static void mpc_cache_check ( unsigned long checking_time ) ;
static struct llc_snap_hdr llc_snap_mpoa_ctrl = {
0xaa , 0xaa , 0x03 ,
{ 0x00 , 0x00 , 0x5e } ,
{ 0x00 , 0x03 } /* For MPOA control PDUs */
2007-02-09 23:24:29 +09:00
} ;
2005-04-16 15:20:36 -07:00
static struct llc_snap_hdr llc_snap_mpoa_data = {
0xaa , 0xaa , 0x03 ,
{ 0x00 , 0x00 , 0x00 } ,
{ 0x08 , 0x00 } /* This is for IP PDUs only */
2007-02-09 23:24:29 +09:00
} ;
2005-04-16 15:20:36 -07:00
static struct llc_snap_hdr llc_snap_mpoa_data_tagged = {
0xaa , 0xaa , 0x03 ,
{ 0x00 , 0x00 , 0x00 } ,
{ 0x88 , 0x4c } /* This is for tagged data PDUs */
2007-02-09 23:24:29 +09:00
} ;
2005-04-16 15:20:36 -07:00
static struct notifier_block mpoa_notifier = {
mpoa_event_listener ,
NULL ,
0
} ;
struct mpoa_client * mpcs = NULL ; /* FIXME */
static struct atm_mpoa_qos * qos_head = NULL ;
2005-09-09 13:10:40 -07:00
static DEFINE_TIMER ( mpc_timer , NULL , 0 , 0 ) ;
2005-04-16 15:20:36 -07:00
static struct mpoa_client * find_mpc_by_itfnum ( int itf )
{
struct mpoa_client * mpc ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc = mpcs ; /* our global linked list */
while ( mpc ! = NULL ) {
if ( mpc - > dev_num = = itf )
return mpc ;
2007-02-09 23:24:29 +09:00
mpc = mpc - > next ;
2005-04-16 15:20:36 -07:00
}
return NULL ; /* not found */
}
static struct mpoa_client * find_mpc_by_vcc ( struct atm_vcc * vcc )
{
struct mpoa_client * mpc ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc = mpcs ; /* our global linked list */
while ( mpc ! = NULL ) {
if ( mpc - > mpoad_vcc = = vcc )
return mpc ;
mpc = mpc - > next ;
}
return NULL ; /* not found */
}
static struct mpoa_client * find_mpc_by_lec ( struct net_device * dev )
{
struct mpoa_client * mpc ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc = mpcs ; /* our global linked list */
while ( mpc ! = NULL ) {
if ( mpc - > dev = = dev )
return mpc ;
mpc = mpc - > next ;
}
return NULL ; /* not found */
}
/*
* Functions for managing QoS list
*/
/*
* Overwrites the old entry or makes a new one .
*/
2006-11-14 21:11:29 -08:00
struct atm_mpoa_qos * atm_mpoa_add_qos ( __be32 dst_ip , struct atm_qos * qos )
2005-04-16 15:20:36 -07:00
{
struct atm_mpoa_qos * entry ;
entry = atm_mpoa_search_qos ( dst_ip ) ;
if ( entry ! = NULL ) {
entry - > qos = * qos ;
return entry ;
}
entry = kmalloc ( sizeof ( struct atm_mpoa_qos ) , GFP_KERNEL ) ;
if ( entry = = NULL ) {
printk ( " mpoa: atm_mpoa_add_qos: out of memory \n " ) ;
return entry ;
}
entry - > ipaddr = dst_ip ;
entry - > qos = * qos ;
entry - > next = qos_head ;
qos_head = entry ;
return entry ;
}
2006-11-14 21:11:29 -08:00
struct atm_mpoa_qos * atm_mpoa_search_qos ( __be32 dst_ip )
2005-04-16 15:20:36 -07:00
{
struct atm_mpoa_qos * qos ;
qos = qos_head ;
while ( qos ! = NULL ) {
if ( qos - > ipaddr = = dst_ip ) {
break ;
}
qos = qos - > next ;
}
return qos ;
2007-02-09 23:24:29 +09:00
}
2005-04-16 15:20:36 -07:00
/*
* Returns 0 for failure
*/
int atm_mpoa_delete_qos ( struct atm_mpoa_qos * entry )
{
struct atm_mpoa_qos * curr ;
if ( entry = = NULL ) return 0 ;
if ( entry = = qos_head ) {
qos_head = qos_head - > next ;
kfree ( entry ) ;
return 1 ;
}
curr = qos_head ;
while ( curr ! = NULL ) {
if ( curr - > next = = entry ) {
curr - > next = entry - > next ;
kfree ( entry ) ;
return 1 ;
}
curr = curr - > next ;
}
return 0 ;
}
/* this is buggered - we need locking for qos_head */
void atm_mpoa_disp_qos ( struct seq_file * m )
{
struct atm_mpoa_qos * qos ;
qos = qos_head ;
seq_printf ( m , " QoS entries for shortcuts: \n " ) ;
seq_printf ( m , " IP address \n TX:max_pcr pcr min_pcr max_cdv max_sdu \n RX:max_pcr pcr min_pcr max_cdv max_sdu \n " ) ;
while ( qos ! = NULL ) {
2008-10-31 00:54:56 -07:00
seq_printf ( m , " %pI4 \n %-7d %-7d %-7d %-7d %-7d \n %-7d %-7d %-7d %-7d %-7d \n " ,
& qos - > ipaddr ,
2005-04-16 15:20:36 -07:00
qos - > qos . txtp . max_pcr , qos - > qos . txtp . pcr , qos - > qos . txtp . min_pcr , qos - > qos . txtp . max_cdv , qos - > qos . txtp . max_sdu ,
qos - > qos . rxtp . max_pcr , qos - > qos . rxtp . pcr , qos - > qos . rxtp . min_pcr , qos - > qos . rxtp . max_cdv , qos - > qos . rxtp . max_sdu ) ;
qos = qos - > next ;
}
}
static struct net_device * find_lec_by_itfnum ( int itf )
{
struct net_device * dev ;
char name [ IFNAMSIZ ] ;
sprintf ( name , " lec%d " , itf ) ;
2007-09-17 11:56:21 -07:00
dev = dev_get_by_name ( & init_net , name ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
return dev ;
}
static struct mpoa_client * alloc_mpc ( void )
{
struct mpoa_client * mpc ;
2006-07-21 14:51:30 -07:00
mpc = kzalloc ( sizeof ( struct mpoa_client ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( mpc = = NULL )
return NULL ;
rwlock_init ( & mpc - > ingress_lock ) ;
rwlock_init ( & mpc - > egress_lock ) ;
mpc - > next = mpcs ;
atm_mpoa_init_cache ( mpc ) ;
mpc - > parameters . mpc_p1 = MPC_P1 ;
mpc - > parameters . mpc_p2 = MPC_P2 ;
memset ( mpc - > parameters . mpc_p3 , 0 , sizeof ( mpc - > parameters . mpc_p3 ) ) ;
mpc - > parameters . mpc_p4 = MPC_P4 ;
2007-02-09 23:24:29 +09:00
mpc - > parameters . mpc_p5 = MPC_P5 ;
2005-04-16 15:20:36 -07:00
mpc - > parameters . mpc_p6 = MPC_P6 ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpcs = mpc ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
return mpc ;
}
/*
*
* start_mpc ( ) puts the MPC on line . All the packets destined
2007-02-09 23:24:29 +09:00
* to the lec underneath us are now being monitored and
2005-04-16 15:20:36 -07:00
* shortcuts will be established .
*
*/
static void start_mpc ( struct mpoa_client * mpc , struct net_device * dev )
{
2007-02-09 23:24:29 +09:00
dprintk ( " mpoa: (%s) start_mpc: \n " , mpc - > dev - > name ) ;
2009-03-20 19:35:28 +00:00
if ( ! dev - > netdev_ops )
printk ( " mpoa: (%s) start_mpc not starting \n " , dev - > name ) ;
else {
mpc - > old_ops = dev - > netdev_ops ;
mpc - > new_ops = * mpc - > old_ops ;
mpc - > new_ops . ndo_start_xmit = mpc_send_packet ;
dev - > netdev_ops = & mpc - > new_ops ;
2005-04-16 15:20:36 -07:00
}
}
static void stop_mpc ( struct mpoa_client * mpc )
{
2009-03-20 19:35:28 +00:00
struct net_device * dev = mpc - > dev ;
2007-02-09 23:24:29 +09:00
dprintk ( " mpoa: (%s) stop_mpc: " , mpc - > dev - > name ) ;
2005-04-16 15:20:36 -07:00
/* Lets not nullify lec device's dev->hard_start_xmit */
2009-03-20 19:35:28 +00:00
if ( dev - > netdev_ops ! = & mpc - > new_ops ) {
2005-04-16 15:20:36 -07:00
dprintk ( " mpc already stopped, not fatal \n " ) ;
return ;
}
dprintk ( " \n " ) ;
2007-02-09 23:24:29 +09:00
2009-03-20 19:35:28 +00:00
dev - > netdev_ops = mpc - > old_ops ;
mpc - > old_ops = NULL ;
/* close_shortcuts(mpc); ??? FIXME */
2005-04-16 15:20:36 -07:00
}
static const char * mpoa_device_type_string ( char type ) __attribute__ ( ( unused ) ) ;
static const char * mpoa_device_type_string ( char type )
{
switch ( type ) {
case NON_MPOA :
return " non-MPOA device " ;
break ;
case MPS :
return " MPS " ;
break ;
case MPC :
return " MPC " ;
break ;
case MPS_AND_MPC :
return " both MPS and MPC " ;
break ;
default :
return " unspecified (non-MPOA) device " ;
break ;
}
return " " ; /* not reached */
}
/*
2008-12-08 01:14:16 -08:00
* lec device calls this via its netdev_priv ( dev ) - > lane2_ops
* - > associate_indicator ( ) when it sees a TLV in LE_ARP packet .
2005-04-16 15:20:36 -07:00
* We fill in the pointer above when we see a LANE2 lec initializing
* See LANE2 spec 3.1 .5
*
* Quite a big and ugly function but when you look at it
* all it does is to try to locate and parse MPOA Device
* Type TLV .
* We give our lec a pointer to this function and when the
* lec sees a TLV it uses the pointer to call this function .
*
*/
2008-07-30 16:31:46 -07:00
static void lane2_assoc_ind ( struct net_device * dev , const u8 * mac_addr ,
const u8 * tlvs , u32 sizeoftlvs )
2005-04-16 15:20:36 -07:00
{
uint32_t type ;
uint8_t length , mpoa_device_type , number_of_mps_macs ;
2008-07-30 16:31:46 -07:00
const uint8_t * end_of_tlvs ;
2005-04-16 15:20:36 -07:00
struct mpoa_client * mpc ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpoa_device_type = number_of_mps_macs = 0 ; /* silence gcc */
dprintk ( " mpoa: (%s) lane2_assoc_ind: received TLV(s), " , dev - > name ) ;
dprintk ( " total length of all TLVs %d \n " , sizeoftlvs ) ;
mpc = find_mpc_by_lec ( dev ) ; /* Sampo-Fix: moved here from below */
if ( mpc = = NULL ) {
printk ( " mpoa: (%s) lane2_assoc_ind: no mpc \n " , dev - > name ) ;
return ;
}
end_of_tlvs = tlvs + sizeoftlvs ;
while ( end_of_tlvs - tlvs > = 5 ) {
type = ( tlvs [ 0 ] < < 24 ) | ( tlvs [ 1 ] < < 16 ) | ( tlvs [ 2 ] < < 8 ) | tlvs [ 3 ] ;
length = tlvs [ 4 ] ;
tlvs + = 5 ;
dprintk ( " type 0x%x length %02x \n " , type , length ) ;
if ( tlvs + length > end_of_tlvs ) {
printk ( " TLV value extends past its buffer, aborting parse \n " ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
if ( type = = 0 ) {
printk ( " mpoa: (%s) lane2_assoc_ind: TLV type was 0, returning \n " , dev - > name ) ;
return ;
}
if ( type ! = TLV_MPOA_DEVICE_TYPE ) {
tlvs + = length ;
continue ; /* skip other TLVs */
}
mpoa_device_type = * tlvs + + ;
number_of_mps_macs = * tlvs + + ;
dprintk ( " mpoa: (%s) MPOA device type '%s', " , dev - > name , mpoa_device_type_string ( mpoa_device_type ) ) ;
if ( mpoa_device_type = = MPS_AND_MPC & &
length < ( 42 + number_of_mps_macs * ETH_ALEN ) ) { /* :) */
printk ( " \n mpoa: (%s) lane2_assoc_ind: short MPOA Device Type TLV \n " ,
dev - > name ) ;
continue ;
}
if ( ( mpoa_device_type = = MPS | | mpoa_device_type = = MPC )
& & length < 22 + number_of_mps_macs * ETH_ALEN ) {
printk ( " \n mpoa: (%s) lane2_assoc_ind: short MPOA Device Type TLV \n " ,
dev - > name ) ;
continue ;
}
if ( mpoa_device_type ! = MPS & & mpoa_device_type ! = MPS_AND_MPC ) {
dprintk ( " ignoring non-MPS device \n " ) ;
if ( mpoa_device_type = = MPC ) tlvs + = 20 ;
continue ; /* we are only interested in MPSs */
}
if ( number_of_mps_macs = = 0 & & mpoa_device_type = = MPS_AND_MPC ) {
printk ( " \n mpoa: (%s) lane2_assoc_ind: MPS_AND_MPC has zero MACs \n " , dev - > name ) ;
continue ; /* someone should read the spec */
}
dprintk ( " this MPS has %d MAC addresses \n " , number_of_mps_macs ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
/* ok, now we can go and tell our daemon the control address of MPS */
send_set_mps_ctrl_addr ( tlvs , mpc ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
tlvs = copy_macs ( mpc , mac_addr , tlvs , number_of_mps_macs , mpoa_device_type ) ;
if ( tlvs = = NULL ) return ;
}
if ( end_of_tlvs - tlvs ! = 0 )
printk ( " mpoa: (%s) lane2_assoc_ind: ignoring %Zd bytes of trailing TLV carbage \n " ,
dev - > name , end_of_tlvs - tlvs ) ;
return ;
}
/*
* Store at least advertizing router ' s MAC address
* plus the possible MAC address ( es ) to mpc - > mps_macs .
* For a freshly allocated MPOA client mpc - > mps_macs = = 0.
*/
2008-07-30 16:31:46 -07:00
static const uint8_t * copy_macs ( struct mpoa_client * mpc ,
const uint8_t * router_mac ,
const uint8_t * tlvs , uint8_t mps_macs ,
uint8_t device_type )
2005-04-16 15:20:36 -07:00
{
int num_macs ;
num_macs = ( mps_macs > 1 ) ? mps_macs : 1 ;
if ( mpc - > number_of_mps_macs ! = num_macs ) { /* need to reallocate? */
if ( mpc - > number_of_mps_macs ! = 0 ) kfree ( mpc - > mps_macs ) ;
mpc - > number_of_mps_macs = 0 ;
mpc - > mps_macs = kmalloc ( num_macs * ETH_ALEN , GFP_KERNEL ) ;
if ( mpc - > mps_macs = = NULL ) {
printk ( " mpoa: (%s) copy_macs: out of mem \n " , mpc - > dev - > name ) ;
return NULL ;
}
}
memcpy ( mpc - > mps_macs , router_mac , ETH_ALEN ) ;
tlvs + = 20 ; if ( device_type = = MPS_AND_MPC ) tlvs + = 20 ;
if ( mps_macs > 0 )
memcpy ( mpc - > mps_macs , tlvs , mps_macs * ETH_ALEN ) ;
tlvs + = mps_macs * ETH_ALEN ;
mpc - > number_of_mps_macs = num_macs ;
return tlvs ;
}
static int send_via_shortcut ( struct sk_buff * skb , struct mpoa_client * mpc )
{
in_cache_entry * entry ;
struct iphdr * iph ;
char * buff ;
2006-11-14 21:11:29 -08:00
__be32 ipaddr = 0 ;
2005-04-16 15:20:36 -07:00
static struct {
struct llc_snap_hdr hdr ;
2006-11-14 21:11:29 -08:00
__be32 tag ;
2005-04-16 15:20:36 -07:00
} tagged_llc_snap_hdr = {
{ 0xaa , 0xaa , 0x03 , { 0x00 , 0x00 , 0x00 } , { 0x88 , 0x4c } } ,
0
} ;
buff = skb - > data + mpc - > dev - > hard_header_len ;
iph = ( struct iphdr * ) buff ;
ipaddr = iph - > daddr ;
2007-02-09 23:24:29 +09:00
ddprintk ( " mpoa: (%s) send_via_shortcut: ipaddr 0x%x \n " , mpc - > dev - > name , ipaddr ) ;
2005-04-16 15:20:36 -07:00
entry = mpc - > in_ops - > get ( ipaddr , mpc ) ;
if ( entry = = NULL ) {
entry = mpc - > in_ops - > add_entry ( ipaddr , mpc ) ;
if ( entry ! = NULL ) mpc - > in_ops - > put ( entry ) ;
return 1 ;
}
if ( mpc - > in_ops - > cache_hit ( entry , mpc ) ! = OPEN ) { /* threshold not exceeded or VCC not ready */
2007-02-09 23:24:29 +09:00
ddprintk ( " mpoa: (%s) send_via_shortcut: cache_hit: returns != OPEN \n " , mpc - > dev - > name ) ;
2005-04-16 15:20:36 -07:00
mpc - > in_ops - > put ( entry ) ;
return 1 ;
}
2007-02-09 23:24:29 +09:00
ddprintk ( " mpoa: (%s) send_via_shortcut: using shortcut \n " , mpc - > dev - > name ) ;
2005-04-16 15:20:36 -07:00
/* MPOA spec A.1.4, MPOA client must decrement IP ttl at least by one */
if ( iph - > ttl < = 1 ) {
2007-02-09 23:24:29 +09:00
ddprintk ( " mpoa: (%s) send_via_shortcut: IP ttl = %u, using LANE \n " , mpc - > dev - > name , iph - > ttl ) ;
2005-04-16 15:20:36 -07:00
mpc - > in_ops - > put ( entry ) ;
return 1 ;
}
iph - > ttl - - ;
iph - > check = 0 ;
iph - > check = ip_fast_csum ( ( unsigned char * ) iph , iph - > ihl ) ;
if ( entry - > ctrl_info . tag ! = 0 ) {
ddprintk ( " mpoa: (%s) send_via_shortcut: adding tag 0x%x \n " , mpc - > dev - > name , entry - > ctrl_info . tag ) ;
tagged_llc_snap_hdr . tag = entry - > ctrl_info . tag ;
skb_pull ( skb , ETH_HLEN ) ; /* get rid of Eth header */
skb_push ( skb , sizeof ( tagged_llc_snap_hdr ) ) ; /* add LLC/SNAP header */
2007-03-31 11:55:19 -03:00
skb_copy_to_linear_data ( skb , & tagged_llc_snap_hdr ,
sizeof ( tagged_llc_snap_hdr ) ) ;
2005-04-16 15:20:36 -07:00
} else {
skb_pull ( skb , ETH_HLEN ) ; /* get rid of Eth header */
skb_push ( skb , sizeof ( struct llc_snap_hdr ) ) ; /* add LLC/SNAP header + tag */
2007-03-31 11:55:19 -03:00
skb_copy_to_linear_data ( skb , & llc_snap_mpoa_data ,
sizeof ( struct llc_snap_hdr ) ) ;
2005-04-16 15:20:36 -07:00
}
atomic_add ( skb - > truesize , & sk_atm ( entry - > shortcut ) - > sk_wmem_alloc ) ;
ATM_SKB ( skb ) - > atm_options = entry - > shortcut - > atm_options ;
entry - > shortcut - > send ( entry - > shortcut , skb ) ;
entry - > packets_fwded + + ;
mpc - > in_ops - > put ( entry ) ;
return 0 ;
}
/*
* Probably needs some error checks and locking , not sure . . .
*/
static int mpc_send_packet ( struct sk_buff * skb , struct net_device * dev )
{
struct mpoa_client * mpc ;
struct ethhdr * eth ;
int i = 0 ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc = find_mpc_by_lec ( dev ) ; /* this should NEVER fail */
if ( mpc = = NULL ) {
printk ( " mpoa: (%s) mpc_send_packet: no MPC found \n " , dev - > name ) ;
goto non_ip ;
}
eth = ( struct ethhdr * ) skb - > data ;
if ( eth - > h_proto ! = htons ( ETH_P_IP ) )
goto non_ip ; /* Multi-Protocol Over ATM :-) */
2008-01-09 03:51:59 -08:00
/* Weed out funny packets (e.g., AF_PACKET or raw). */
if ( skb - > len < ETH_HLEN + sizeof ( struct iphdr ) )
goto non_ip ;
skb_set_network_header ( skb , ETH_HLEN ) ;
if ( skb - > len < ETH_HLEN + ip_hdr ( skb ) - > ihl * 4 | | ip_hdr ( skb ) - > ihl < 5 )
goto non_ip ;
2005-04-16 15:20:36 -07:00
while ( i < mpc - > number_of_mps_macs ) {
2006-01-09 16:01:43 -08:00
if ( ! compare_ether_addr ( eth - > h_dest , ( mpc - > mps_macs + i * ETH_ALEN ) ) )
2005-04-16 15:20:36 -07:00
if ( send_via_shortcut ( skb , mpc ) = = 0 ) /* try shortcut */
return 0 ; /* success! */
i + + ;
}
non_ip :
2009-03-20 19:35:28 +00:00
return mpc - > old_ops - > ndo_start_xmit ( skb , dev ) ;
2005-04-16 15:20:36 -07:00
}
static int atm_mpoa_vcc_attach ( struct atm_vcc * vcc , void __user * arg )
{
int bytes_left ;
struct mpoa_client * mpc ;
struct atmmpc_ioc ioc_data ;
in_cache_entry * in_entry ;
2006-11-14 21:11:29 -08:00
__be32 ipaddr ;
2005-04-16 15:20:36 -07:00
bytes_left = copy_from_user ( & ioc_data , arg , sizeof ( struct atmmpc_ioc ) ) ;
if ( bytes_left ! = 0 ) {
printk ( " mpoa: mpc_vcc_attach: Short read (missed %d bytes) from userland \n " , bytes_left ) ;
return - EFAULT ;
}
ipaddr = ioc_data . ipaddr ;
if ( ioc_data . dev_num < 0 | | ioc_data . dev_num > = MAX_LEC_ITF )
return - EINVAL ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc = find_mpc_by_itfnum ( ioc_data . dev_num ) ;
if ( mpc = = NULL )
return - EINVAL ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
if ( ioc_data . type = = MPC_SOCKET_INGRESS ) {
in_entry = mpc - > in_ops - > get ( ipaddr , mpc ) ;
if ( in_entry = = NULL | | in_entry - > entry_state < INGRESS_RESOLVED ) {
printk ( " mpoa: (%s) mpc_vcc_attach: did not find RESOLVED entry from ingress cache \n " ,
mpc - > dev - > name ) ;
if ( in_entry ! = NULL ) mpc - > in_ops - > put ( in_entry ) ;
return - EINVAL ;
}
2008-10-31 00:54:56 -07:00
printk ( " mpoa: (%s) mpc_vcc_attach: attaching ingress SVC, entry = %pI4 \n " ,
mpc - > dev - > name , & in_entry - > ctrl_info . in_dst_ip ) ;
2005-04-16 15:20:36 -07:00
in_entry - > shortcut = vcc ;
mpc - > in_ops - > put ( in_entry ) ;
} else {
printk ( " mpoa: (%s) mpc_vcc_attach: attaching egress SVC \n " , mpc - > dev - > name ) ;
}
vcc - > proto_data = mpc - > dev ;
vcc - > push = mpc_push ;
return 0 ;
}
/*
*
*/
static void mpc_vcc_close ( struct atm_vcc * vcc , struct net_device * dev )
{
struct mpoa_client * mpc ;
in_cache_entry * in_entry ;
eg_cache_entry * eg_entry ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc = find_mpc_by_lec ( dev ) ;
if ( mpc = = NULL ) {
printk ( " mpoa: (%s) mpc_vcc_close: close for unknown MPC \n " , dev - > name ) ;
return ;
}
dprintk ( " mpoa: (%s) mpc_vcc_close: \n " , dev - > name ) ;
in_entry = mpc - > in_ops - > get_by_vcc ( vcc , mpc ) ;
if ( in_entry ) {
2008-10-31 00:54:56 -07:00
dprintk ( " mpoa: (%s) mpc_vcc_close: ingress SVC closed ip = %pI4 \n " ,
mpc - > dev - > name , & in_entry - > ctrl_info . in_dst_ip ) ;
2005-04-16 15:20:36 -07:00
in_entry - > shortcut = NULL ;
mpc - > in_ops - > put ( in_entry ) ;
}
eg_entry = mpc - > eg_ops - > get_by_vcc ( vcc , mpc ) ;
if ( eg_entry ) {
dprintk ( " mpoa: (%s) mpc_vcc_close: egress SVC closed \n " , mpc - > dev - > name ) ;
eg_entry - > shortcut = NULL ;
mpc - > eg_ops - > put ( eg_entry ) ;
}
if ( in_entry = = NULL & & eg_entry = = NULL )
dprintk ( " mpoa: (%s) mpc_vcc_close: unused vcc closed \n " , dev - > name ) ;
return ;
}
static void mpc_push ( struct atm_vcc * vcc , struct sk_buff * skb )
{
struct net_device * dev = ( struct net_device * ) vcc - > proto_data ;
struct sk_buff * new_skb ;
eg_cache_entry * eg ;
struct mpoa_client * mpc ;
2006-11-14 21:11:29 -08:00
__be32 tag ;
2005-04-16 15:20:36 -07:00
char * tmp ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
ddprintk ( " mpoa: (%s) mpc_push: \n " , dev - > name ) ;
if ( skb = = NULL ) {
dprintk ( " mpoa: (%s) mpc_push: null skb, closing VCC \n " , dev - > name ) ;
mpc_vcc_close ( vcc , dev ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
skb - > dev = dev ;
if ( memcmp ( skb - > data , & llc_snap_mpoa_ctrl , sizeof ( struct llc_snap_hdr ) ) = = 0 ) {
struct sock * sk = sk_atm ( vcc ) ;
dprintk ( " mpoa: (%s) mpc_push: control packet arrived \n " , dev - > name ) ;
/* Pass control packets to daemon */
skb_queue_tail ( & sk - > sk_receive_queue , skb ) ;
sk - > sk_data_ready ( sk , skb - > len ) ;
return ;
}
/* data coming over the shortcut */
atm_return ( vcc , skb - > truesize ) ;
mpc = find_mpc_by_lec ( dev ) ;
if ( mpc = = NULL ) {
printk ( " mpoa: (%s) mpc_push: unknown MPC \n " , dev - > name ) ;
return ;
}
if ( memcmp ( skb - > data , & llc_snap_mpoa_data_tagged , sizeof ( struct llc_snap_hdr ) ) = = 0 ) { /* MPOA tagged data */
ddprintk ( " mpoa: (%s) mpc_push: tagged data packet arrived \n " , dev - > name ) ;
} else if ( memcmp ( skb - > data , & llc_snap_mpoa_data , sizeof ( struct llc_snap_hdr ) ) = = 0 ) { /* MPOA data */
printk ( " mpoa: (%s) mpc_push: non-tagged data packet arrived \n " , dev - > name ) ;
printk ( " mpc_push: non-tagged data unsupported, purging \n " ) ;
dev_kfree_skb_any ( skb ) ;
return ;
} else {
printk ( " mpoa: (%s) mpc_push: garbage arrived, purging \n " , dev - > name ) ;
dev_kfree_skb_any ( skb ) ;
return ;
}
tmp = skb - > data + sizeof ( struct llc_snap_hdr ) ;
2006-11-14 21:11:29 -08:00
tag = * ( __be32 * ) tmp ;
2005-04-16 15:20:36 -07:00
eg = mpc - > eg_ops - > get_by_tag ( tag , mpc ) ;
if ( eg = = NULL ) {
printk ( " mpoa: (%s) mpc_push: Didn't find egress cache entry, tag = %u \n " ,
dev - > name , tag ) ;
purge_egress_shortcut ( vcc , NULL ) ;
dev_kfree_skb_any ( skb ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
/*
* See if ingress MPC is using shortcut we opened as a return channel .
* This means we have a bi - directional vcc opened by us .
2007-02-09 23:24:29 +09:00
*/
2005-04-16 15:20:36 -07:00
if ( eg - > shortcut = = NULL ) {
eg - > shortcut = vcc ;
printk ( " mpoa: (%s) mpc_push: egress SVC in use \n " , dev - > name ) ;
}
skb_pull ( skb , sizeof ( struct llc_snap_hdr ) + sizeof ( tag ) ) ; /* get rid of LLC/SNAP header */
new_skb = skb_realloc_headroom ( skb , eg - > ctrl_info . DH_length ) ; /* LLC/SNAP is shorter than MAC header :( */
dev_kfree_skb_any ( skb ) ;
if ( new_skb = = NULL ) {
mpc - > eg_ops - > put ( eg ) ;
return ;
}
skb_push ( new_skb , eg - > ctrl_info . DH_length ) ; /* add MAC header */
2007-03-31 11:55:19 -03:00
skb_copy_to_linear_data ( new_skb , eg - > ctrl_info . DLL_header ,
eg - > ctrl_info . DH_length ) ;
2005-04-16 15:20:36 -07:00
new_skb - > protocol = eth_type_trans ( new_skb , dev ) ;
2007-04-10 20:45:18 -07:00
skb_reset_network_header ( new_skb ) ;
2005-04-16 15:20:36 -07:00
2007-04-20 22:47:35 -07:00
eg - > latest_ip_addr = ip_hdr ( new_skb ) - > saddr ;
2005-04-16 15:20:36 -07:00
eg - > packets_rcvd + + ;
mpc - > eg_ops - > put ( eg ) ;
memset ( ATM_SKB ( skb ) , 0 , sizeof ( struct atm_skb_data ) ) ;
netif_rx ( new_skb ) ;
return ;
}
static struct atmdev_ops mpc_ops = { /* only send is required */
. close = mpoad_close ,
. send = msg_from_mpoad
} ;
static struct atm_dev mpc_dev = {
. ops = & mpc_ops ,
. type = " mpc " ,
. number = 42 ,
2007-04-26 01:37:44 -07:00
. lock = __SPIN_LOCK_UNLOCKED ( mpc_dev . lock )
2005-04-16 15:20:36 -07:00
/* members not explicitly initialised will be 0 */
} ;
static int atm_mpoa_mpoad_attach ( struct atm_vcc * vcc , int arg )
{
struct mpoa_client * mpc ;
struct lec_priv * priv ;
int err ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
if ( mpcs = = NULL ) {
init_timer ( & mpc_timer ) ;
mpc_timer_refresh ( ) ;
/* This lets us now how our LECs are doing */
err = register_netdevice_notifier ( & mpoa_notifier ) ;
if ( err < 0 ) {
del_timer ( & mpc_timer ) ;
return err ;
}
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc = find_mpc_by_itfnum ( arg ) ;
if ( mpc = = NULL ) {
dprintk ( " mpoa: mpoad_attach: allocating new mpc for itf %d \n " , arg ) ;
mpc = alloc_mpc ( ) ;
if ( mpc = = NULL )
return - ENOMEM ;
mpc - > dev_num = arg ;
mpc - > dev = find_lec_by_itfnum ( arg ) ; /* NULL if there was no lec */
}
if ( mpc - > mpoad_vcc ) {
printk ( " mpoa: mpoad_attach: mpoad is already present for itf %d \n " , arg ) ;
return - EADDRINUSE ;
}
if ( mpc - > dev ) { /* check if the lec is LANE2 capable */
2008-11-12 23:39:10 -08:00
priv = netdev_priv ( mpc - > dev ) ;
2005-04-16 15:20:36 -07:00
if ( priv - > lane_version < 2 ) {
dev_put ( mpc - > dev ) ;
mpc - > dev = NULL ;
} else
2007-02-09 23:24:29 +09:00
priv - > lane2_ops - > associate_indicator = lane2_assoc_ind ;
2005-04-16 15:20:36 -07:00
}
mpc - > mpoad_vcc = vcc ;
vcc - > dev = & mpc_dev ;
vcc_insert_socket ( sk_atm ( vcc ) ) ;
set_bit ( ATM_VF_META , & vcc - > flags ) ;
set_bit ( ATM_VF_READY , & vcc - > flags ) ;
if ( mpc - > dev ) {
char empty [ ATM_ESA_LEN ] ;
memset ( empty , 0 , ATM_ESA_LEN ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
start_mpc ( mpc , mpc - > dev ) ;
/* set address if mpcd e.g. gets killed and restarted.
* If we do not do it now we have to wait for the next LE_ARP
*/
if ( memcmp ( mpc - > mps_ctrl_addr , empty , ATM_ESA_LEN ) ! = 0 )
send_set_mps_ctrl_addr ( mpc - > mps_ctrl_addr , mpc ) ;
}
__module_get ( THIS_MODULE ) ;
return arg ;
}
2008-07-30 16:31:46 -07:00
static void send_set_mps_ctrl_addr ( const char * addr , struct mpoa_client * mpc )
2005-04-16 15:20:36 -07:00
{
struct k_message mesg ;
memcpy ( mpc - > mps_ctrl_addr , addr , ATM_ESA_LEN ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mesg . type = SET_MPS_CTRL_ADDR ;
memcpy ( mesg . MPS_ctrl , addr , ATM_ESA_LEN ) ;
msg_to_mpoad ( & mesg , mpc ) ;
return ;
}
static void mpoad_close ( struct atm_vcc * vcc )
{
struct mpoa_client * mpc ;
struct sk_buff * skb ;
mpc = find_mpc_by_vcc ( vcc ) ;
if ( mpc = = NULL ) {
printk ( " mpoa: mpoad_close: did not find MPC \n " ) ;
return ;
}
if ( ! mpc - > mpoad_vcc ) {
printk ( " mpoa: mpoad_close: close for non-present mpoad \n " ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
mpc - > mpoad_vcc = NULL ;
if ( mpc - > dev ) {
2008-11-12 23:39:10 -08:00
struct lec_priv * priv = netdev_priv ( mpc - > dev ) ;
2005-04-16 15:20:36 -07:00
priv - > lane2_ops - > associate_indicator = NULL ;
stop_mpc ( mpc ) ;
dev_put ( mpc - > dev ) ;
}
mpc - > in_ops - > destroy_cache ( mpc ) ;
mpc - > eg_ops - > destroy_cache ( mpc ) ;
while ( ( skb = skb_dequeue ( & sk_atm ( vcc ) - > sk_receive_queue ) ) ) {
atm_return ( vcc , skb - > truesize ) ;
kfree_skb ( skb ) ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
printk ( " mpoa: (%s) going down \n " ,
( mpc - > dev ) ? mpc - > dev - > name : " <unknown> " ) ;
module_put ( THIS_MODULE ) ;
return ;
}
/*
*
*/
static int msg_from_mpoad ( struct atm_vcc * vcc , struct sk_buff * skb )
{
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
struct mpoa_client * mpc = find_mpc_by_vcc ( vcc ) ;
struct k_message * mesg = ( struct k_message * ) skb - > data ;
atomic_sub ( skb - > truesize , & sk_atm ( vcc ) - > sk_wmem_alloc ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
if ( mpc = = NULL ) {
printk ( " mpoa: msg_from_mpoad: no mpc found \n " ) ;
return 0 ;
}
dprintk ( " mpoa: (%s) msg_from_mpoad: " , ( mpc - > dev ) ? mpc - > dev - > name : " <unknown> " ) ;
switch ( mesg - > type ) {
case MPOA_RES_REPLY_RCVD :
dprintk ( " mpoa_res_reply_rcvd \n " ) ;
MPOA_res_reply_rcvd ( mesg , mpc ) ;
break ;
case MPOA_TRIGGER_RCVD :
dprintk ( " mpoa_trigger_rcvd \n " ) ;
MPOA_trigger_rcvd ( mesg , mpc ) ;
break ;
case INGRESS_PURGE_RCVD :
dprintk ( " nhrp_purge_rcvd \n " ) ;
ingress_purge_rcvd ( mesg , mpc ) ;
break ;
case EGRESS_PURGE_RCVD :
dprintk ( " egress_purge_reply_rcvd \n " ) ;
egress_purge_rcvd ( mesg , mpc ) ;
break ;
case MPS_DEATH :
dprintk ( " mps_death \n " ) ;
mps_death ( mesg , mpc ) ;
break ;
case CACHE_IMPOS_RCVD :
dprintk ( " cache_impos_rcvd \n " ) ;
MPOA_cache_impos_rcvd ( mesg , mpc ) ;
break ;
case SET_MPC_CTRL_ADDR :
dprintk ( " set_mpc_ctrl_addr \n " ) ;
set_mpc_ctrl_addr_rcvd ( mesg , mpc ) ;
break ;
case SET_MPS_MAC_ADDR :
dprintk ( " set_mps_mac_addr \n " ) ;
set_mps_mac_addr_rcvd ( mesg , mpc ) ;
break ;
case CLEAN_UP_AND_EXIT :
dprintk ( " clean_up_and_exit \n " ) ;
clean_up ( mesg , mpc , DIE ) ;
break ;
case RELOAD :
dprintk ( " reload \n " ) ;
clean_up ( mesg , mpc , RELOAD ) ;
break ;
case SET_MPC_PARAMS :
dprintk ( " set_mpc_params \n " ) ;
mpc - > parameters = mesg - > content . params ;
break ;
default :
dprintk ( " unknown message %d \n " , mesg - > type ) ;
break ;
}
kfree_skb ( skb ) ;
return 0 ;
}
/* Remember that this function may not do things that sleep */
int msg_to_mpoad ( struct k_message * mesg , struct mpoa_client * mpc )
{
struct sk_buff * skb ;
struct sock * sk ;
if ( mpc = = NULL | | ! mpc - > mpoad_vcc ) {
printk ( " mpoa: msg_to_mpoad: mesg %d to a non-existent mpoad \n " , mesg - > type ) ;
return - ENXIO ;
}
skb = alloc_skb ( sizeof ( struct k_message ) , GFP_ATOMIC ) ;
if ( skb = = NULL )
return - ENOMEM ;
skb_put ( skb , sizeof ( struct k_message ) ) ;
2007-03-31 11:55:19 -03:00
skb_copy_to_linear_data ( skb , mesg , sizeof ( * mesg ) ) ;
2005-04-16 15:20:36 -07:00
atm_force_charge ( mpc - > mpoad_vcc , skb - > truesize ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
sk = sk_atm ( mpc - > mpoad_vcc ) ;
skb_queue_tail ( & sk - > sk_receive_queue , skb ) ;
sk - > sk_data_ready ( sk , skb - > len ) ;
return 0 ;
}
static int mpoa_event_listener ( struct notifier_block * mpoa_notifier , unsigned long event , void * dev_ptr )
{
struct net_device * dev ;
struct mpoa_client * mpc ;
struct lec_priv * priv ;
dev = ( struct net_device * ) dev_ptr ;
2007-09-12 13:02:17 +02:00
2008-07-19 22:34:43 -07:00
if ( ! net_eq ( dev_net ( dev ) , & init_net ) )
2007-09-12 13:02:17 +02:00
return NOTIFY_DONE ;
2005-04-16 15:20:36 -07:00
if ( dev - > name = = NULL | | strncmp ( dev - > name , " lec " , 3 ) )
return NOTIFY_DONE ; /* we are only interested in lec:s */
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
switch ( event ) {
case NETDEV_REGISTER : /* a new lec device was allocated */
2008-11-12 23:39:10 -08:00
priv = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
if ( priv - > lane_version < 2 )
break ;
priv - > lane2_ops - > associate_indicator = lane2_assoc_ind ;
mpc = find_mpc_by_itfnum ( priv - > itfnum ) ;
if ( mpc = = NULL ) {
dprintk ( " mpoa: mpoa_event_listener: allocating new mpc for %s \n " ,
dev - > name ) ;
mpc = alloc_mpc ( ) ;
if ( mpc = = NULL ) {
printk ( " mpoa: mpoa_event_listener: no new mpc " ) ;
break ;
}
}
mpc - > dev_num = priv - > itfnum ;
mpc - > dev = dev ;
dev_hold ( dev ) ;
dprintk ( " mpoa: (%s) was initialized \n " , dev - > name ) ;
break ;
case NETDEV_UNREGISTER :
/* the lec device was deallocated */
mpc = find_mpc_by_lec ( dev ) ;
if ( mpc = = NULL )
break ;
dprintk ( " mpoa: device (%s) was deallocated \n " , dev - > name ) ;
stop_mpc ( mpc ) ;
dev_put ( mpc - > dev ) ;
mpc - > dev = NULL ;
break ;
case NETDEV_UP :
/* the dev was ifconfig'ed up */
mpc = find_mpc_by_lec ( dev ) ;
if ( mpc = = NULL )
break ;
if ( mpc - > mpoad_vcc ! = NULL ) {
start_mpc ( mpc , dev ) ;
}
break ;
case NETDEV_DOWN :
/* the dev was ifconfig'ed down */
/* this means that the flow of packets from the
* upper layer stops
*/
mpc = find_mpc_by_lec ( dev ) ;
if ( mpc = = NULL )
break ;
if ( mpc - > mpoad_vcc ! = NULL ) {
stop_mpc ( mpc ) ;
}
break ;
case NETDEV_REBOOT :
case NETDEV_CHANGE :
case NETDEV_CHANGEMTU :
case NETDEV_CHANGEADDR :
case NETDEV_GOING_DOWN :
break ;
default :
break ;
}
return NOTIFY_DONE ;
}
/*
* Functions which are called after a message is received from mpcd .
* Msg is reused on purpose .
*/
static void MPOA_trigger_rcvd ( struct k_message * msg , struct mpoa_client * mpc )
{
2006-11-14 21:11:29 -08:00
__be32 dst_ip = msg - > content . in_info . in_dst_ip ;
2005-04-16 15:20:36 -07:00
in_cache_entry * entry ;
entry = mpc - > in_ops - > get ( dst_ip , mpc ) ;
if ( entry = = NULL ) {
entry = mpc - > in_ops - > add_entry ( dst_ip , mpc ) ;
entry - > entry_state = INGRESS_RESOLVING ;
msg - > type = SND_MPOA_RES_RQST ;
msg - > content . in_info = entry - > ctrl_info ;
msg_to_mpoad ( msg , mpc ) ;
do_gettimeofday ( & ( entry - > reply_wait ) ) ;
mpc - > in_ops - > put ( entry ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
if ( entry - > entry_state = = INGRESS_INVALID ) {
entry - > entry_state = INGRESS_RESOLVING ;
msg - > type = SND_MPOA_RES_RQST ;
msg - > content . in_info = entry - > ctrl_info ;
msg_to_mpoad ( msg , mpc ) ;
do_gettimeofday ( & ( entry - > reply_wait ) ) ;
mpc - > in_ops - > put ( entry ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
printk ( " mpoa: (%s) MPOA_trigger_rcvd: entry already in resolving state \n " ,
( mpc - > dev ) ? mpc - > dev - > name : " <unknown> " ) ;
mpc - > in_ops - > put ( entry ) ;
return ;
}
/*
* Things get complicated because we have to check if there ' s an egress
2007-02-09 23:24:29 +09:00
* shortcut with suitable traffic parameters we could use .
2005-04-16 15:20:36 -07:00
*/
static void check_qos_and_open_shortcut ( struct k_message * msg , struct mpoa_client * client , in_cache_entry * entry )
{
2006-11-14 21:11:29 -08:00
__be32 dst_ip = msg - > content . in_info . in_dst_ip ;
2005-04-16 15:20:36 -07:00
struct atm_mpoa_qos * qos = atm_mpoa_search_qos ( dst_ip ) ;
eg_cache_entry * eg_entry = client - > eg_ops - > get_by_src_ip ( dst_ip , client ) ;
if ( eg_entry & & eg_entry - > shortcut ) {
if ( eg_entry - > shortcut - > qos . txtp . traffic_class &
msg - > qos . txtp . traffic_class &
( qos ? qos - > qos . txtp . traffic_class : ATM_UBR | ATM_CBR ) ) {
if ( eg_entry - > shortcut - > qos . txtp . traffic_class = = ATM_UBR )
entry - > shortcut = eg_entry - > shortcut ;
else if ( eg_entry - > shortcut - > qos . txtp . max_pcr > 0 )
entry - > shortcut = eg_entry - > shortcut ;
}
2007-02-09 23:24:29 +09:00
if ( entry - > shortcut ) {
2008-10-31 00:54:56 -07:00
dprintk ( " mpoa: (%s) using egress SVC to reach %pI4 \n " ,
client - > dev - > name , & dst_ip ) ;
2005-04-16 15:20:36 -07:00
client - > eg_ops - > put ( eg_entry ) ;
return ;
}
}
if ( eg_entry ! = NULL )
client - > eg_ops - > put ( eg_entry ) ;
/* No luck in the egress cache we must open an ingress SVC */
msg - > type = OPEN_INGRESS_SVC ;
if ( qos & & ( qos - > qos . txtp . traffic_class = = msg - > qos . txtp . traffic_class ) )
{
msg - > qos = qos - > qos ;
printk ( " mpoa: (%s) trying to get a CBR shortcut \n " , client - > dev - > name ) ;
2007-02-09 23:24:29 +09:00
}
2005-04-16 15:20:36 -07:00
else memset ( & msg - > qos , 0 , sizeof ( struct atm_qos ) ) ;
msg_to_mpoad ( msg , client ) ;
return ;
}
static void MPOA_res_reply_rcvd ( struct k_message * msg , struct mpoa_client * mpc )
{
2006-11-14 21:11:29 -08:00
__be32 dst_ip = msg - > content . in_info . in_dst_ip ;
2005-04-16 15:20:36 -07:00
in_cache_entry * entry = mpc - > in_ops - > get ( dst_ip , mpc ) ;
2006-06-26 00:01:58 -07:00
2008-10-31 00:54:56 -07:00
dprintk ( " mpoa: (%s) MPOA_res_reply_rcvd: ip %pI4 \n " ,
mpc - > dev - > name , & dst_ip ) ;
2005-04-16 15:20:36 -07:00
ddprintk ( " mpoa: (%s) MPOA_res_reply_rcvd() entry = %p " , mpc - > dev - > name , entry ) ;
if ( entry = = NULL ) {
printk ( " \n mpoa: (%s) ARGH, received res. reply for an entry that doesn't exist. \n " , mpc - > dev - > name ) ;
return ;
}
2007-02-09 23:24:29 +09:00
ddprintk ( " entry_state = %d " , entry - > entry_state ) ;
2005-04-16 15:20:36 -07:00
if ( entry - > entry_state = = INGRESS_RESOLVED ) {
printk ( " \n mpoa: (%s) MPOA_res_reply_rcvd for RESOLVED entry! \n " , mpc - > dev - > name ) ;
mpc - > in_ops - > put ( entry ) ;
return ;
}
entry - > ctrl_info = msg - > content . in_info ;
do_gettimeofday ( & ( entry - > tv ) ) ;
do_gettimeofday ( & ( entry - > reply_wait ) ) ; /* Used in refreshing func from now on */
entry - > refresh_time = 0 ;
ddprintk ( " entry->shortcut = %p \n " , entry - > shortcut ) ;
if ( entry - > entry_state = = INGRESS_RESOLVING & & entry - > shortcut ! = NULL ) {
2007-02-09 23:24:29 +09:00
entry - > entry_state = INGRESS_RESOLVED ;
2005-04-16 15:20:36 -07:00
mpc - > in_ops - > put ( entry ) ;
return ; /* Shortcut already open... */
}
if ( entry - > shortcut ! = NULL ) {
printk ( " mpoa: (%s) MPOA_res_reply_rcvd: entry->shortcut != NULL, impossible! \n " ,
mpc - > dev - > name ) ;
mpc - > in_ops - > put ( entry ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
check_qos_and_open_shortcut ( msg , mpc , entry ) ;
entry - > entry_state = INGRESS_RESOLVED ;
mpc - > in_ops - > put ( entry ) ;
return ;
}
static void ingress_purge_rcvd ( struct k_message * msg , struct mpoa_client * mpc )
{
2006-11-14 21:11:29 -08:00
__be32 dst_ip = msg - > content . in_info . in_dst_ip ;
__be32 mask = msg - > ip_mask ;
2005-04-16 15:20:36 -07:00
in_cache_entry * entry = mpc - > in_ops - > get_with_mask ( dst_ip , mpc , mask ) ;
if ( entry = = NULL ) {
2008-10-31 00:54:56 -07:00
printk ( " mpoa: (%s) ingress_purge_rcvd: purge for a non-existing entry, ip = %pI4 \n " ,
mpc - > dev - > name , & dst_ip ) ;
2005-04-16 15:20:36 -07:00
return ;
}
do {
2008-10-31 00:54:56 -07:00
dprintk ( " mpoa: (%s) ingress_purge_rcvd: removing an ingress entry, ip = %pI4 \n " ,
mpc - > dev - > name , & dst_ip ) ;
2005-04-16 15:20:36 -07:00
write_lock_bh ( & mpc - > ingress_lock ) ;
mpc - > in_ops - > remove_entry ( entry , mpc ) ;
write_unlock_bh ( & mpc - > ingress_lock ) ;
mpc - > in_ops - > put ( entry ) ;
entry = mpc - > in_ops - > get_with_mask ( dst_ip , mpc , mask ) ;
} while ( entry ! = NULL ) ;
return ;
2007-02-09 23:24:29 +09:00
}
2005-04-16 15:20:36 -07:00
static void egress_purge_rcvd ( struct k_message * msg , struct mpoa_client * mpc )
{
2006-11-14 21:11:29 -08:00
__be32 cache_id = msg - > content . eg_info . cache_id ;
2005-04-16 15:20:36 -07:00
eg_cache_entry * entry = mpc - > eg_ops - > get_by_cache_id ( cache_id , mpc ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
if ( entry = = NULL ) {
dprintk ( " mpoa: (%s) egress_purge_rcvd: purge for a non-existing entry \n " , mpc - > dev - > name ) ;
return ;
}
write_lock_irq ( & mpc - > egress_lock ) ;
mpc - > eg_ops - > remove_entry ( entry , mpc ) ;
write_unlock_irq ( & mpc - > egress_lock ) ;
mpc - > eg_ops - > put ( entry ) ;
return ;
2007-02-09 23:24:29 +09:00
}
2005-04-16 15:20:36 -07:00
static void purge_egress_shortcut ( struct atm_vcc * vcc , eg_cache_entry * entry )
{
struct sock * sk ;
struct k_message * purge_msg ;
struct sk_buff * skb ;
dprintk ( " mpoa: purge_egress_shortcut: entering \n " ) ;
if ( vcc = = NULL ) {
printk ( " mpoa: purge_egress_shortcut: vcc == NULL \n " ) ;
return ;
}
skb = alloc_skb ( sizeof ( struct k_message ) , GFP_ATOMIC ) ;
if ( skb = = NULL ) {
printk ( " mpoa: purge_egress_shortcut: out of memory \n " ) ;
return ;
}
skb_put ( skb , sizeof ( struct k_message ) ) ;
memset ( skb - > data , 0 , sizeof ( struct k_message ) ) ;
purge_msg = ( struct k_message * ) skb - > data ;
purge_msg - > type = DATA_PLANE_PURGE ;
if ( entry ! = NULL )
purge_msg - > content . eg_info = entry - > ctrl_info ;
atm_force_charge ( vcc , skb - > truesize ) ;
sk = sk_atm ( vcc ) ;
skb_queue_tail ( & sk - > sk_receive_queue , skb ) ;
sk - > sk_data_ready ( sk , skb - > len ) ;
dprintk ( " mpoa: purge_egress_shortcut: exiting: \n " ) ;
return ;
}
/*
* Our MPS died . Tell our daemon to send NHRP data plane purge to each
* of the egress shortcuts we have .
*/
static void mps_death ( struct k_message * msg , struct mpoa_client * mpc )
{
eg_cache_entry * entry ;
dprintk ( " mpoa: (%s) mps_death: \n " , mpc - > dev - > name ) ;
if ( memcmp ( msg - > MPS_ctrl , mpc - > mps_ctrl_addr , ATM_ESA_LEN ) ) {
printk ( " mpoa: (%s) mps_death: wrong MPS \n " , mpc - > dev - > name ) ;
return ;
}
/* FIXME: This knows too much of the cache structure */
read_lock_irq ( & mpc - > egress_lock ) ;
entry = mpc - > eg_cache ;
while ( entry ! = NULL ) {
purge_egress_shortcut ( entry - > shortcut , entry ) ;
entry = entry - > next ;
}
read_unlock_irq ( & mpc - > egress_lock ) ;
mpc - > in_ops - > destroy_cache ( mpc ) ;
mpc - > eg_ops - > destroy_cache ( mpc ) ;
return ;
}
static void MPOA_cache_impos_rcvd ( struct k_message * msg , struct mpoa_client * mpc )
{
uint16_t holding_time ;
eg_cache_entry * entry = mpc - > eg_ops - > get_by_cache_id ( msg - > content . eg_info . cache_id , mpc ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
holding_time = msg - > content . eg_info . holding_time ;
dprintk ( " mpoa: (%s) MPOA_cache_impos_rcvd: entry = %p, holding_time = %u \n " ,
mpc - > dev - > name , entry , holding_time ) ;
if ( entry = = NULL & & holding_time ) {
entry = mpc - > eg_ops - > add_entry ( msg , mpc ) ;
mpc - > eg_ops - > put ( entry ) ;
return ;
}
if ( holding_time ) {
mpc - > eg_ops - > update ( entry , holding_time ) ;
return ;
}
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
write_lock_irq ( & mpc - > egress_lock ) ;
mpc - > eg_ops - > remove_entry ( entry , mpc ) ;
write_unlock_irq ( & mpc - > egress_lock ) ;
mpc - > eg_ops - > put ( entry ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
return ;
}
static void set_mpc_ctrl_addr_rcvd ( struct k_message * mesg , struct mpoa_client * mpc )
{
struct lec_priv * priv ;
int i , retval ;
uint8_t tlv [ 4 + 1 + 1 + 1 + ATM_ESA_LEN ] ;
tlv [ 0 ] = 00 ; tlv [ 1 ] = 0xa0 ; tlv [ 2 ] = 0x3e ; tlv [ 3 ] = 0x2a ; /* type */
tlv [ 4 ] = 1 + 1 + ATM_ESA_LEN ; /* length */
tlv [ 5 ] = 0x02 ; /* MPOA client */
tlv [ 6 ] = 0x00 ; /* number of MPS MAC addresses */
memcpy ( & tlv [ 7 ] , mesg - > MPS_ctrl , ATM_ESA_LEN ) ; /* MPC ctrl ATM addr */
memcpy ( mpc - > our_ctrl_addr , mesg - > MPS_ctrl , ATM_ESA_LEN ) ;
dprintk ( " mpoa: (%s) setting MPC ctrl ATM address to " ,
( mpc - > dev ) ? mpc - > dev - > name : " <unknown> " ) ;
for ( i = 7 ; i < sizeof ( tlv ) ; i + + )
dprintk ( " %02x " , tlv [ i ] ) ;
dprintk ( " \n " ) ;
if ( mpc - > dev ) {
2008-11-12 23:39:10 -08:00
priv = netdev_priv ( mpc - > dev ) ;
2005-04-16 15:20:36 -07:00
retval = priv - > lane2_ops - > associate_req ( mpc - > dev , mpc - > dev - > dev_addr , tlv , sizeof ( tlv ) ) ;
if ( retval = = 0 )
printk ( " mpoa: (%s) MPOA device type TLV association failed \n " , mpc - > dev - > name ) ;
retval = priv - > lane2_ops - > resolve ( mpc - > dev , NULL , 1 , NULL , NULL ) ;
if ( retval < 0 )
printk ( " mpoa: (%s) targetless LE_ARP request failed \n " , mpc - > dev - > name ) ;
}
return ;
}
static void set_mps_mac_addr_rcvd ( struct k_message * msg , struct mpoa_client * client )
{
if ( client - > number_of_mps_macs )
kfree ( client - > mps_macs ) ;
client - > number_of_mps_macs = 0 ;
2006-11-21 01:14:33 -02:00
client - > mps_macs = kmemdup ( msg - > MPS_ctrl , ETH_ALEN , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( client - > mps_macs = = NULL ) {
printk ( " mpoa: set_mps_mac_addr_rcvd: out of memory \n " ) ;
return ;
}
client - > number_of_mps_macs = 1 ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
return ;
}
/*
* purge egress cache and tell daemon to ' action ' ( DIE , RELOAD )
*/
static void clean_up ( struct k_message * msg , struct mpoa_client * mpc , int action )
{
eg_cache_entry * entry ;
msg - > type = SND_EGRESS_PURGE ;
/* FIXME: This knows too much of the cache structure */
read_lock_irq ( & mpc - > egress_lock ) ;
entry = mpc - > eg_cache ;
while ( entry ! = NULL ) {
msg - > content . eg_info = entry - > ctrl_info ;
dprintk ( " mpoa: cache_id %u \n " , entry - > ctrl_info . cache_id ) ;
msg_to_mpoad ( msg , mpc ) ;
entry = entry - > next ;
}
read_unlock_irq ( & mpc - > egress_lock ) ;
msg - > type = action ;
msg_to_mpoad ( msg , mpc ) ;
return ;
}
static void mpc_timer_refresh ( void )
{
mpc_timer . expires = jiffies + ( MPC_P2 * HZ ) ;
mpc_timer . data = mpc_timer . expires ;
mpc_timer . function = mpc_cache_check ;
add_timer ( & mpc_timer ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
return ;
}
static void mpc_cache_check ( unsigned long checking_time )
{
struct mpoa_client * mpc = mpcs ;
static unsigned long previous_resolving_check_time ;
static unsigned long previous_refresh_time ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
while ( mpc ! = NULL ) {
mpc - > in_ops - > clear_count ( mpc ) ;
mpc - > eg_ops - > clear_expired ( mpc ) ;
if ( checking_time - previous_resolving_check_time > mpc - > parameters . mpc_p4 * HZ ) {
mpc - > in_ops - > check_resolving ( mpc ) ;
previous_resolving_check_time = checking_time ;
}
if ( checking_time - previous_refresh_time > mpc - > parameters . mpc_p5 * HZ ) {
mpc - > in_ops - > refresh ( mpc ) ;
previous_refresh_time = checking_time ;
}
mpc = mpc - > next ;
}
mpc_timer_refresh ( ) ;
2007-02-09 23:24:29 +09:00
2005-04-16 15:20:36 -07:00
return ;
}
static int atm_mpoa_ioctl ( struct socket * sock , unsigned int cmd , unsigned long arg )
{
int err = 0 ;
struct atm_vcc * vcc = ATM_SD ( sock ) ;
if ( cmd ! = ATMMPC_CTRL & & cmd ! = ATMMPC_DATA )
return - ENOIOCTLCMD ;
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
switch ( cmd ) {
case ATMMPC_CTRL :
err = atm_mpoa_mpoad_attach ( vcc , ( int ) arg ) ;
if ( err > = 0 )
sock - > state = SS_CONNECTED ;
break ;
case ATMMPC_DATA :
err = atm_mpoa_vcc_attach ( vcc , ( void __user * ) arg ) ;
break ;
default :
break ;
}
return err ;
}
static struct atm_ioctl atm_ioctl_ops = {
. owner = THIS_MODULE ,
. ioctl = atm_mpoa_ioctl ,
} ;
static __init int atm_mpoa_init ( void )
{
register_atm_ioctl ( & atm_ioctl_ops ) ;
if ( mpc_proc_init ( ) ! = 0 )
printk ( KERN_INFO " mpoa: failed to initialize /proc/mpoa \n " ) ;
printk ( " mpc.c: " __DATE__ " " __TIME__ " initialized \n " ) ;
return 0 ;
}
static void __exit atm_mpoa_cleanup ( void )
{
struct mpoa_client * mpc , * tmp ;
struct atm_mpoa_qos * qos , * nextqos ;
struct lec_priv * priv ;
mpc_proc_clean ( ) ;
del_timer ( & mpc_timer ) ;
unregister_netdevice_notifier ( & mpoa_notifier ) ;
deregister_atm_ioctl ( & atm_ioctl_ops ) ;
mpc = mpcs ;
mpcs = NULL ;
while ( mpc ! = NULL ) {
tmp = mpc - > next ;
if ( mpc - > dev ! = NULL ) {
stop_mpc ( mpc ) ;
2008-11-12 23:39:10 -08:00
priv = netdev_priv ( mpc - > dev ) ;
2005-04-16 15:20:36 -07:00
if ( priv - > lane2_ops ! = NULL )
priv - > lane2_ops - > associate_indicator = NULL ;
}
ddprintk ( " mpoa: cleanup_module: about to clear caches \n " ) ;
mpc - > in_ops - > destroy_cache ( mpc ) ;
mpc - > eg_ops - > destroy_cache ( mpc ) ;
ddprintk ( " mpoa: cleanup_module: caches cleared \n " ) ;
kfree ( mpc - > mps_macs ) ;
memset ( mpc , 0 , sizeof ( struct mpoa_client ) ) ;
ddprintk ( " mpoa: cleanup_module: about to kfree %p \n " , mpc ) ;
kfree ( mpc ) ;
ddprintk ( " mpoa: cleanup_module: next mpc is at %p \n " , tmp ) ;
mpc = tmp ;
}
qos = qos_head ;
qos_head = NULL ;
while ( qos ! = NULL ) {
nextqos = qos - > next ;
dprintk ( " mpoa: cleanup_module: freeing qos entry %p \n " , qos ) ;
kfree ( qos ) ;
qos = nextqos ;
}
return ;
}
module_init ( atm_mpoa_init ) ;
module_exit ( atm_mpoa_cleanup ) ;
MODULE_LICENSE ( " GPL " ) ;