2005-04-17 02:20:36 +04:00
/** -*- linux-c -*- ***********************************************************
* Linux PPP over Ethernet ( PPPoX / PPPoE ) Sockets
*
* PPPoX - - - Generic PPP encapsulation socket family
* PPPoE - - - PPP over Ethernet ( RFC 2516 )
*
*
* Version : 0.7 .0
*
2007-03-03 00:16:56 +03:00
* 07022 8 : Fix to allow multiple sessions with same remote MAC and same
* session id by including the local device ifindex in the
* tuple identifying a session . This also ensures packets can ' t
* be injected into a session from interfaces other than the one
* specified by userspace . Florian Zumbiehl < florz @ florz . de >
* ( Oh , BTW , this one is YYMMDD , in case you were wondering . . . )
2005-04-17 02:20:36 +04:00
* 220102 : Fix module use count on failure in pppoe_create , pppox_sk - acme
* 030700 : Fixed connect logic to allow for disconnect .
* 270700 : Fixed potential SMP problems ; we must protect against
* simultaneous invocation of ppp_input
* and ppp_unregister_channel .
* 040 800 : Respect reference count mechanisms on net - devices .
* 200800 : fix kfree ( skb ) in pppoe_rcv ( acme )
* Module reference count is decremented in the right spot now ,
* guards against sock_put not actually freeing the sk
* in pppoe_release .
* 051000 : Initialization cleanup .
* 111100 : Fix recvmsg .
* 050101 : Fix PADT procesing .
* 140501 : Use pppoe_rcv_core to handle all backlog . ( Alexey )
* 170701 : Do not lock_sock with rwlock held . ( DaveM )
* Ignore discovery frames if user has socket
* locked . ( DaveM )
* Ignore return value of dev_queue_xmit in __pppoe_xmit
* or else we may kfree an SKB twice . ( DaveM )
* 190701 : When doing copies of skb ' s in __pppoe_xmit , always delete
* the original skb that was passed in on success , never on
* failure . Delete the copy of the skb on failure to avoid
* a memory leak .
* 081001 : Misc . cleanup ( licence string , non - blocking , prevent
* reference of device on close ) .
* 121301 : New ppp channels interface ; cannot unregister a channel
* from interrupts . Thus , we mark the socket as a ZOMBIE
* and do the unregistration later .
* 081002 : seq_file support for proc stuff - acme
* 111602 : Merge all 2.4 fixes into 2.5 / 2.6 tree . Label 2.5 / 2.6
* as version 0.7 . Spacing cleanup .
* Author : Michal Ostrowski < mostrows @ speakeasy . net >
* Contributors :
* Arnaldo Carvalho de Melo < acme @ conectiva . com . br >
* David S . Miller ( davem @ redhat . com )
*
* License :
* 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 .
*
*/
# include <linux/string.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/net.h>
# include <linux/inetdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/init.h>
# include <linux/if_ether.h>
# include <linux/if_pppox.h>
# include <linux/ppp_channel.h>
# include <linux/ppp_defs.h>
# include <linux/if_ppp.h>
# include <linux/notifier.h>
# include <linux/file.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
2009-01-22 02:54:54 +03:00
# include <linux/nsproxy.h>
2007-09-12 14:01:34 +04:00
# include <net/net_namespace.h>
2009-01-22 02:54:54 +03:00
# include <net/netns/generic.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include <asm/uaccess.h>
# define PPPOE_HASH_BITS 4
2009-01-22 02:54:15 +03:00
# define PPPOE_HASH_SIZE (1 << PPPOE_HASH_BITS)
# define PPPOE_HASH_MASK (PPPOE_HASH_SIZE - 1)
2005-04-17 02:20:36 +04:00
static int pppoe_ioctl ( struct socket * sock , unsigned int cmd , unsigned long arg ) ;
static int pppoe_xmit ( struct ppp_channel * chan , struct sk_buff * skb ) ;
static int __pppoe_xmit ( struct sock * sk , struct sk_buff * skb ) ;
2005-12-28 07:57:40 +03:00
static const struct proto_ops pppoe_ops ;
2009-01-22 02:54:15 +03:00
static struct ppp_channel_ops pppoe_chan_ops ;
2009-01-22 02:54:54 +03:00
/* per-net private data for this module */
2009-02-14 14:13:52 +03:00
static int pppoe_net_id ;
2009-01-22 02:54:54 +03:00
struct pppoe_net {
/*
* we could use _single_ hash table for all
* nets by injecting net id into the hash but
* it would increase hash chains and add
* a few additional math comparations messy
* as well , moreover in case of SMP less locking
* controversy here
*/
struct pppox_sock * hash_table [ PPPOE_HASH_SIZE ] ;
rwlock_t hash_lock ;
} ;
/* to eliminate a race btw pppoe_flush_dev and pppoe_release */
static DEFINE_SPINLOCK ( flush_lock ) ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:15 +03:00
/*
* PPPoE could be in the following stages :
* 1 ) Discovery stage ( to obtain remote MAC and Session ID )
* 2 ) Session stage ( MAC and SID are known )
*
* Ethernet frames have a special tag for this but
* we use simplier approach based on session id
*/
static inline bool stage_session ( __be16 sid )
{
return sid ! = 0 ;
}
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:54 +03:00
static inline struct pppoe_net * pppoe_pernet ( struct net * net )
{
BUG_ON ( ! net ) ;
return net_generic ( net , pppoe_net_id ) ;
}
2005-04-17 02:20:36 +04:00
static inline int cmp_2_addr ( struct pppoe_addr * a , struct pppoe_addr * b )
{
2009-01-22 02:54:54 +03:00
return a - > sid = = b - > sid & & ! memcmp ( a - > remote , b - > remote , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
}
2007-08-23 10:55:33 +04:00
static inline int cmp_addr ( struct pppoe_addr * a , __be16 sid , char * addr )
2005-04-17 02:20:36 +04:00
{
2009-01-22 02:54:54 +03:00
return a - > sid = = sid & & ! memcmp ( a - > remote , addr , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-22 02:54:15 +03:00
# if 8 % PPPOE_HASH_BITS
2007-08-01 00:47:57 +04:00
# error 8 must be a multiple of PPPOE_HASH_BITS
# endif
2007-08-23 10:55:33 +04:00
static int hash_item ( __be16 sid , unsigned char * addr )
2005-04-17 02:20:36 +04:00
{
2007-08-01 00:47:57 +04:00
unsigned char hash = 0 ;
unsigned int i ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:15 +03:00
for ( i = 0 ; i < ETH_ALEN ; i + + )
2007-08-01 00:47:57 +04:00
hash ^ = addr [ i ] ;
2009-01-22 02:54:15 +03:00
for ( i = 0 ; i < sizeof ( sid_t ) * 8 ; i + = 8 )
hash ^ = ( __force __u32 ) sid > > i ;
for ( i = 8 ; ( i > > = 1 ) > = PPPOE_HASH_BITS ; )
hash ^ = hash > > i ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:15 +03:00
return hash & PPPOE_HASH_MASK ;
2005-04-17 02:20:36 +04:00
}
/**********************************************************************
*
* Set / get / delete / rehash items ( internal versions )
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-01-22 02:54:54 +03:00
static struct pppox_sock * __get_item ( struct pppoe_net * pn , __be16 sid ,
unsigned char * addr , int ifindex )
2005-04-17 02:20:36 +04:00
{
int hash = hash_item ( sid , addr ) ;
struct pppox_sock * ret ;
2009-01-22 02:54:54 +03:00
ret = pn - > hash_table [ hash ] ;
2009-01-22 02:54:15 +03:00
while ( ret ) {
if ( cmp_addr ( & ret - > pppoe_pa , sid , addr ) & &
ret - > pppoe_ifindex = = ifindex )
return ret ;
2005-04-17 02:20:36 +04:00
ret = ret - > next ;
2009-01-22 02:54:15 +03:00
}
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:15 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2009-01-22 02:54:54 +03:00
static int __set_item ( struct pppoe_net * pn , struct pppox_sock * po )
2005-04-17 02:20:36 +04:00
{
int hash = hash_item ( po - > pppoe_pa . sid , po - > pppoe_pa . remote ) ;
struct pppox_sock * ret ;
2009-01-22 02:54:54 +03:00
ret = pn - > hash_table [ hash ] ;
2005-04-17 02:20:36 +04:00
while ( ret ) {
2009-01-22 02:54:15 +03:00
if ( cmp_2_addr ( & ret - > pppoe_pa , & po - > pppoe_pa ) & &
ret - > pppoe_ifindex = = po - > pppoe_ifindex )
2005-04-17 02:20:36 +04:00
return - EALREADY ;
ret = ret - > next ;
}
2009-01-22 02:54:54 +03:00
po - > next = pn - > hash_table [ hash ] ;
pn - > hash_table [ hash ] = po ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-01-22 02:54:54 +03:00
static struct pppox_sock * __delete_item ( struct pppoe_net * pn , __be16 sid ,
char * addr , int ifindex )
2005-04-17 02:20:36 +04:00
{
int hash = hash_item ( sid , addr ) ;
struct pppox_sock * ret , * * src ;
2009-01-22 02:54:54 +03:00
ret = pn - > hash_table [ hash ] ;
src = & pn - > hash_table [ hash ] ;
2005-04-17 02:20:36 +04:00
while ( ret ) {
2009-01-22 02:54:15 +03:00
if ( cmp_addr ( & ret - > pppoe_pa , sid , addr ) & &
ret - > pppoe_ifindex = = ifindex ) {
2005-04-17 02:20:36 +04:00
* src = ret - > next ;
break ;
}
src = & ret - > next ;
ret = ret - > next ;
}
return ret ;
}
/**********************************************************************
*
* Set / get / delete / rehash items
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-01-22 02:54:54 +03:00
static inline struct pppox_sock * get_item ( struct pppoe_net * pn , __be16 sid ,
unsigned char * addr , int ifindex )
2005-04-17 02:20:36 +04:00
{
struct pppox_sock * po ;
2009-01-22 02:54:54 +03:00
read_lock_bh ( & pn - > hash_lock ) ;
po = __get_item ( pn , sid , addr , ifindex ) ;
2005-04-17 02:20:36 +04:00
if ( po )
sock_hold ( sk_pppox ( po ) ) ;
2009-01-22 02:54:54 +03:00
read_unlock_bh ( & pn - > hash_lock ) ;
2005-04-17 02:20:36 +04:00
return po ;
}
2009-01-22 02:54:54 +03:00
static inline struct pppox_sock * get_item_by_addr ( struct net * net ,
struct sockaddr_pppox * sp )
2005-04-17 02:20:36 +04:00
{
2007-04-21 03:56:31 +04:00
struct net_device * dev ;
2009-01-22 02:54:54 +03:00
struct pppoe_net * pn ;
struct pppox_sock * pppox_sock ;
2007-03-03 00:16:56 +03:00
int ifindex ;
2009-01-22 02:54:54 +03:00
dev = dev_get_by_name ( net , sp - > sa_addr . pppoe . dev ) ;
2009-01-22 02:54:15 +03:00
if ( ! dev )
2007-03-03 00:16:56 +03:00
return NULL ;
2009-01-22 02:54:54 +03:00
2007-03-03 00:16:56 +03:00
ifindex = dev - > ifindex ;
2009-01-22 02:54:54 +03:00
pn = net_generic ( net , pppoe_net_id ) ;
pppox_sock = get_item ( pn , sp - > sa_addr . pppoe . sid ,
sp - > sa_addr . pppoe . remote , ifindex ) ;
2007-03-03 00:16:56 +03:00
dev_put ( dev ) ;
2009-01-22 02:54:54 +03:00
return pppox_sock ;
2005-04-17 02:20:36 +04:00
}
2009-01-22 02:54:54 +03:00
static inline struct pppox_sock * delete_item ( struct pppoe_net * pn , __be16 sid ,
char * addr , int ifindex )
2005-04-17 02:20:36 +04:00
{
struct pppox_sock * ret ;
2009-01-22 02:54:54 +03:00
write_lock_bh ( & pn - > hash_lock ) ;
ret = __delete_item ( pn , sid , addr , ifindex ) ;
write_unlock_bh ( & pn - > hash_lock ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
/***************************************************************************
*
* Handler for device events .
* Certain device events require that sockets be unconnected .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void pppoe_flush_dev ( struct net_device * dev )
{
2009-01-22 02:54:54 +03:00
struct pppoe_net * pn ;
int i ;
2005-04-17 02:20:36 +04:00
BUG_ON ( dev = = NULL ) ;
2009-01-22 02:54:54 +03:00
pn = pppoe_pernet ( dev_net ( dev ) ) ;
if ( ! pn ) /* already freed */
return ;
write_lock_bh ( & pn - > hash_lock ) ;
for ( i = 0 ; i < PPPOE_HASH_SIZE ; i + + ) {
struct pppox_sock * po = pn - > hash_table [ i ] ;
2005-04-17 02:20:36 +04:00
while ( po ! = NULL ) {
2009-01-22 02:54:54 +03:00
struct sock * sk ;
2007-04-21 03:59:24 +04:00
if ( po - > pppoe_dev ! = dev ) {
po = po - > next ;
continue ;
}
2009-01-22 02:54:54 +03:00
sk = sk_pppox ( po ) ;
spin_lock ( & flush_lock ) ;
2007-04-21 03:59:24 +04:00
po - > pppoe_dev = NULL ;
2009-01-22 02:54:54 +03:00
spin_unlock ( & flush_lock ) ;
2007-04-21 03:59:24 +04:00
dev_put ( dev ) ;
2005-04-17 02:20:36 +04:00
2007-04-21 03:59:24 +04:00
/* We always grab the socket lock, followed by the
2009-01-22 02:54:54 +03:00
* hash_lock , in that order . Since we should
2007-04-21 03:59:24 +04:00
* hold the sock lock while doing any unbinding ,
* we need to release the lock we ' re holding .
* Hold a reference to the sock so it doesn ' t disappear
* as we ' re jumping between locks .
*/
2005-04-17 02:20:36 +04:00
2007-04-21 03:59:24 +04:00
sock_hold ( sk ) ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:54 +03:00
write_unlock_bh ( & pn - > hash_lock ) ;
2007-04-21 03:59:24 +04:00
lock_sock ( sk ) ;
2005-04-17 02:20:36 +04:00
2007-04-21 03:59:24 +04:00
if ( sk - > sk_state & ( PPPOX_CONNECTED | PPPOX_BOUND ) ) {
pppox_unbind_sock ( sk ) ;
sk - > sk_state = PPPOX_ZOMBIE ;
sk - > sk_state_change ( sk ) ;
}
2005-04-17 02:20:36 +04:00
2007-04-21 03:59:24 +04:00
release_sock ( sk ) ;
sock_put ( sk ) ;
2005-04-17 02:20:36 +04:00
2007-04-21 03:59:24 +04:00
/* Restart scan at the beginning of this hash chain.
* While the lock was dropped the chain contents may
* have changed .
*/
2009-01-22 02:54:54 +03:00
write_lock_bh ( & pn - > hash_lock ) ;
po = pn - > hash_table [ i ] ;
2005-04-17 02:20:36 +04:00
}
}
2009-01-22 02:54:54 +03:00
write_unlock_bh ( & pn - > hash_lock ) ;
2005-04-17 02:20:36 +04:00
}
static int pppoe_device_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
2009-01-22 02:54:54 +03:00
struct net_device * dev = ( struct net_device * ) ptr ;
2007-09-12 15:02:17 +04:00
2005-04-17 02:20:36 +04:00
/* Only look at sockets that are using this specific device. */
switch ( event ) {
case NETDEV_CHANGEMTU :
/* A change in mtu is a bad thing, requiring
* LCP re - negotiation .
*/
case NETDEV_GOING_DOWN :
case NETDEV_DOWN :
/* Find every socket on this device and kill it. */
pppoe_flush_dev ( dev ) ;
break ;
default :
break ;
} ;
return NOTIFY_DONE ;
}
static struct notifier_block pppoe_notifier = {
. notifier_call = pppoe_device_event ,
} ;
/************************************************************************
*
* Do the real work of receiving a PPPoE Session frame .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int pppoe_rcv_core ( struct sock * sk , struct sk_buff * skb )
{
struct pppox_sock * po = pppox_sk ( sk ) ;
2007-04-21 03:56:31 +04:00
struct pppox_sock * relay_po ;
2005-04-17 02:20:36 +04:00
if ( sk - > sk_state & PPPOX_BOUND ) {
ppp_input ( & po - > chan , skb ) ;
} else if ( sk - > sk_state & PPPOX_RELAY ) {
2009-01-22 02:54:54 +03:00
relay_po = get_item_by_addr ( dev_net ( po - > pppoe_dev ) ,
& po - > pppoe_relay ) ;
2005-04-17 02:20:36 +04:00
if ( relay_po = = NULL )
goto abort_kfree ;
if ( ( sk_pppox ( relay_po ) - > sk_state & PPPOX_CONNECTED ) = = 0 )
goto abort_put ;
if ( ! __pppoe_xmit ( sk_pppox ( relay_po ) , skb ) )
goto abort_put ;
} else {
if ( sock_queue_rcv_skb ( sk , skb ) )
goto abort_kfree ;
}
return NET_RX_SUCCESS ;
abort_put :
sock_put ( sk_pppox ( relay_po ) ) ;
abort_kfree :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
/************************************************************************
*
* Receive wrapper called in BH context .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-01-22 02:54:54 +03:00
static int pppoe_rcv ( struct sk_buff * skb , struct net_device * dev ,
struct packet_type * pt , struct net_device * orig_dev )
2005-04-17 02:20:36 +04:00
{
struct pppoe_hdr * ph ;
struct pppox_sock * po ;
2009-01-22 02:54:54 +03:00
struct pppoe_net * pn ;
2008-06-11 01:07:25 +04:00
int len ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:15 +03:00
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( ! skb )
2005-04-17 02:20:36 +04:00
goto out ;
2007-09-17 03:19:20 +04:00
if ( ! pskb_may_pull ( skb , sizeof ( struct pppoe_hdr ) ) )
goto drop ;
2007-03-10 21:56:08 +03:00
ph = pppoe_hdr ( skb ) ;
2008-06-11 01:07:25 +04:00
len = ntohs ( ph - > length ) ;
skb_pull_rcsum ( skb , sizeof ( * ph ) ) ;
if ( skb - > len < len )
goto drop ;
2005-04-17 02:20:36 +04:00
2008-10-31 09:11:44 +03:00
if ( pskb_trim_rcsum ( skb , len ) )
2008-06-11 01:07:25 +04:00
goto drop ;
2009-01-22 02:54:54 +03:00
pn = pppoe_pernet ( dev_net ( dev ) ) ;
po = get_item ( pn , ph - > sid , eth_hdr ( skb ) - > h_source , dev - > ifindex ) ;
2008-10-31 09:11:44 +03:00
if ( ! po )
2008-06-11 01:07:25 +04:00
goto drop ;
return sk_receive_skb ( sk_pppox ( po ) , skb , 0 ) ;
2005-04-17 02:20:36 +04:00
drop :
kfree_skb ( skb ) ;
out :
return NET_RX_DROP ;
}
/************************************************************************
*
* Receive a PPPoE Discovery frame .
* This is solely for detection of PADT frames
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-01-22 02:54:54 +03:00
static int pppoe_disc_rcv ( struct sk_buff * skb , struct net_device * dev ,
struct packet_type * pt , struct net_device * orig_dev )
2005-04-17 02:20:36 +04:00
{
struct pppoe_hdr * ph ;
struct pppox_sock * po ;
2009-01-22 02:54:54 +03:00
struct pppoe_net * pn ;
2007-09-17 22:53:39 +04:00
2009-01-22 02:54:15 +03:00
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( ! skb )
2005-04-17 02:20:36 +04:00
goto out ;
2008-06-11 01:08:25 +04:00
if ( ! pskb_may_pull ( skb , sizeof ( struct pppoe_hdr ) ) )
goto abort ;
2007-03-10 21:56:08 +03:00
ph = pppoe_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( ph - > code ! = PADT_CODE )
goto abort ;
2009-01-22 02:54:54 +03:00
pn = pppoe_pernet ( dev_net ( dev ) ) ;
po = get_item ( pn , ph - > sid , eth_hdr ( skb ) - > h_source , dev - > ifindex ) ;
2005-04-17 02:20:36 +04:00
if ( po ) {
struct sock * sk = sk_pppox ( po ) ;
bh_lock_sock ( sk ) ;
/* If the user has locked the socket, just ignore
* the packet . With the way two rcv protocols hook into
* one socket family type , we cannot ( easily ) distinguish
* what kind of SKB it is during backlog rcv .
*/
if ( sock_owned_by_user ( sk ) = = 0 ) {
/* We're no longer connect at the PPPOE layer,
* and must wait for ppp channel to disconnect us .
*/
sk - > sk_state = PPPOX_ZOMBIE ;
}
bh_unlock_sock ( sk ) ;
sock_put ( sk ) ;
}
abort :
kfree_skb ( skb ) ;
out :
return NET_RX_SUCCESS ; /* Lies... :-) */
}
2009-03-09 11:18:29 +03:00
static struct packet_type pppoes_ptype __read_mostly = {
2009-02-01 11:45:17 +03:00
. type = cpu_to_be16 ( ETH_P_PPP_SES ) ,
2005-04-17 02:20:36 +04:00
. func = pppoe_rcv ,
} ;
2009-03-09 11:18:29 +03:00
static struct packet_type pppoed_ptype __read_mostly = {
2009-02-01 11:45:17 +03:00
. type = cpu_to_be16 ( ETH_P_PPP_DISC ) ,
2005-04-17 02:20:36 +04:00
. func = pppoe_disc_rcv ,
} ;
2009-03-09 11:18:29 +03:00
static struct proto pppoe_sk_proto __read_mostly = {
2005-04-17 02:20:36 +04:00
. name = " PPPOE " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct pppox_sock ) ,
} ;
/***********************************************************************
*
* Initialize a new struct sock .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-09 10:24:22 +04:00
static int pppoe_create ( struct net * net , struct socket * sock )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
2007-11-01 10:39:31 +03:00
sk = sk_alloc ( net , PF_PPPOX , GFP_KERNEL , & pppoe_sk_proto ) ;
2005-04-17 02:20:36 +04:00
if ( ! sk )
2009-01-22 02:54:15 +03:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
sock_init_data ( sock , sk ) ;
2009-01-22 02:54:54 +03:00
sock - > state = SS_UNCONNECTED ;
sock - > ops = & pppoe_ops ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:54 +03:00
sk - > sk_backlog_rcv = pppoe_rcv_core ;
sk - > sk_state = PPPOX_NONE ;
sk - > sk_type = SOCK_STREAM ;
sk - > sk_family = PF_PPPOX ;
sk - > sk_protocol = PX_PROTO_OE ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:15 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int pppoe_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
struct pppox_sock * po ;
2009-01-22 02:54:54 +03:00
struct pppoe_net * pn ;
2005-04-17 02:20:36 +04:00
if ( ! sk )
return 0 ;
2007-04-21 03:59:24 +04:00
lock_sock ( sk ) ;
2009-01-22 02:54:15 +03:00
if ( sock_flag ( sk , SOCK_DEAD ) ) {
2007-04-21 03:59:24 +04:00
release_sock ( sk ) ;
2005-04-17 02:20:36 +04:00
return - EBADF ;
2007-04-21 03:59:24 +04:00
}
2005-04-17 02:20:36 +04:00
pppox_unbind_sock ( sk ) ;
/* Signal the death of the socket. */
sk - > sk_state = PPPOX_DEAD ;
2009-01-22 02:54:54 +03:00
/*
* pppoe_flush_dev could lead to a race with
* this routine so we use flush_lock to eliminate
* such a case ( we only need per - net specific data )
*/
spin_lock ( & flush_lock ) ;
po = pppox_sk ( sk ) ;
if ( ! po - > pppoe_dev ) {
spin_unlock ( & flush_lock ) ;
goto out ;
}
pn = pppoe_pernet ( dev_net ( po - > pppoe_dev ) ) ;
spin_unlock ( & flush_lock ) ;
2007-04-21 03:59:24 +04:00
2009-01-22 02:54:54 +03:00
/*
* protect " po " from concurrent updates
* on pppoe_flush_dev
*/
write_lock_bh ( & pn - > hash_lock ) ;
2007-04-21 03:59:24 +04:00
2005-04-17 02:20:36 +04:00
po = pppox_sk ( sk ) ;
2009-01-22 02:54:54 +03:00
if ( stage_session ( po - > pppoe_pa . sid ) )
__delete_item ( pn , po - > pppoe_pa . sid , po - > pppoe_pa . remote ,
po - > pppoe_ifindex ) ;
2005-04-17 02:20:36 +04:00
2007-04-21 03:59:24 +04:00
if ( po - > pppoe_dev ) {
2005-04-17 02:20:36 +04:00
dev_put ( po - > pppoe_dev ) ;
2007-04-21 03:59:24 +04:00
po - > pppoe_dev = NULL ;
}
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:54 +03:00
write_unlock_bh ( & pn - > hash_lock ) ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:54 +03:00
out :
2005-04-17 02:20:36 +04:00
sock_orphan ( sk ) ;
sock - > sk = NULL ;
skb_queue_purge ( & sk - > sk_receive_queue ) ;
2007-04-21 03:59:24 +04:00
release_sock ( sk ) ;
2005-04-17 02:20:36 +04:00
sock_put ( sk ) ;
2007-04-21 03:56:31 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int pppoe_connect ( struct socket * sock , struct sockaddr * uservaddr ,
int sockaddr_len , int flags )
{
struct sock * sk = sock - > sk ;
2009-01-22 02:54:54 +03:00
struct sockaddr_pppox * sp = ( struct sockaddr_pppox * ) uservaddr ;
2005-04-17 02:20:36 +04:00
struct pppox_sock * po = pppox_sk ( sk ) ;
2009-01-22 02:54:54 +03:00
struct net_device * dev ;
struct pppoe_net * pn ;
2005-04-17 02:20:36 +04:00
int error ;
lock_sock ( sk ) ;
error = - EINVAL ;
if ( sp - > sa_protocol ! = PX_PROTO_OE )
goto end ;
/* Check for already bound sockets */
error = - EBUSY ;
2009-01-22 02:54:15 +03:00
if ( ( sk - > sk_state & PPPOX_CONNECTED ) & &
stage_session ( sp - > sa_addr . pppoe . sid ) )
2005-04-17 02:20:36 +04:00
goto end ;
/* Check for already disconnected sockets, on attempts to disconnect */
error = - EALREADY ;
2009-01-22 02:54:15 +03:00
if ( ( sk - > sk_state & PPPOX_DEAD ) & &
! stage_session ( sp - > sa_addr . pppoe . sid ) )
2005-04-17 02:20:36 +04:00
goto end ;
error = 0 ;
2009-01-22 02:54:15 +03:00
/* Delete the old binding */
if ( stage_session ( po - > pppoe_pa . sid ) ) {
pppox_unbind_sock ( sk ) ;
2009-01-22 02:54:54 +03:00
if ( po - > pppoe_dev ) {
pn = pppoe_pernet ( dev_net ( po - > pppoe_dev ) ) ;
delete_item ( pn , po - > pppoe_pa . sid ,
po - > pppoe_pa . remote , po - > pppoe_ifindex ) ;
2005-04-17 02:20:36 +04:00
dev_put ( po - > pppoe_dev ) ;
2009-01-22 02:54:54 +03:00
}
2005-04-17 02:20:36 +04:00
memset ( sk_pppox ( po ) + 1 , 0 ,
sizeof ( struct pppox_sock ) - sizeof ( struct sock ) ) ;
sk - > sk_state = PPPOX_NONE ;
}
2009-01-22 02:54:15 +03:00
/* Re-bind in session stage only */
if ( stage_session ( sp - > sa_addr . pppoe . sid ) ) {
2005-04-17 02:20:36 +04:00
error = - ENODEV ;
2009-01-22 02:54:54 +03:00
dev = dev_get_by_name ( sock_net ( sk ) , sp - > sa_addr . pppoe . dev ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
goto end ;
po - > pppoe_dev = dev ;
2007-03-05 03:03:22 +03:00
po - > pppoe_ifindex = dev - > ifindex ;
2009-01-22 02:54:54 +03:00
pn = pppoe_pernet ( dev_net ( dev ) ) ;
write_lock_bh ( & pn - > hash_lock ) ;
2009-01-22 02:54:15 +03:00
if ( ! ( dev - > flags & IFF_UP ) ) {
2009-01-22 02:54:54 +03:00
write_unlock_bh ( & pn - > hash_lock ) ;
2005-04-17 02:20:36 +04:00
goto err_put ;
2007-04-21 03:57:27 +04:00
}
2005-04-17 02:20:36 +04:00
memcpy ( & po - > pppoe_pa ,
& sp - > sa_addr . pppoe ,
sizeof ( struct pppoe_addr ) ) ;
2009-01-22 02:54:54 +03:00
error = __set_item ( pn , po ) ;
write_unlock_bh ( & pn - > hash_lock ) ;
2005-04-17 02:20:36 +04:00
if ( error < 0 )
goto err_put ;
po - > chan . hdrlen = ( sizeof ( struct pppoe_hdr ) +
dev - > hard_header_len ) ;
2006-09-28 03:11:25 +04:00
po - > chan . mtu = dev - > mtu - sizeof ( struct pppoe_hdr ) ;
2005-04-17 02:20:36 +04:00
po - > chan . private = sk ;
po - > chan . ops = & pppoe_chan_ops ;
2009-01-22 02:55:40 +03:00
error = ppp_register_net_channel ( dev_net ( dev ) , & po - > chan ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto err_put ;
sk - > sk_state = PPPOX_CONNECTED ;
}
po - > num = sp - > sa_addr . pppoe . sid ;
2009-01-22 02:54:15 +03:00
end :
2005-04-17 02:20:36 +04:00
release_sock ( sk ) ;
return error ;
err_put :
if ( po - > pppoe_dev ) {
dev_put ( po - > pppoe_dev ) ;
po - > pppoe_dev = NULL ;
}
goto end ;
}
static int pppoe_getname ( struct socket * sock , struct sockaddr * uaddr ,
int * usockaddr_len , int peer )
{
int len = sizeof ( struct sockaddr_pppox ) ;
struct sockaddr_pppox sp ;
sp . sa_family = AF_PPPOX ;
sp . sa_protocol = PX_PROTO_OE ;
memcpy ( & sp . sa_addr . pppoe , & pppox_sk ( sock - > sk ) - > pppoe_pa ,
sizeof ( struct pppoe_addr ) ) ;
memcpy ( uaddr , & sp , len ) ;
* usockaddr_len = len ;
return 0 ;
}
static int pppoe_ioctl ( struct socket * sock , unsigned int cmd ,
unsigned long arg )
{
struct sock * sk = sock - > sk ;
struct pppox_sock * po = pppox_sk ( sk ) ;
2007-07-31 04:48:23 +04:00
int val ;
int err ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case PPPIOCGMRU :
err = - ENXIO ;
if ( ! ( sk - > sk_state & PPPOX_CONNECTED ) )
break ;
err = - EFAULT ;
if ( put_user ( po - > pppoe_dev - > mtu -
sizeof ( struct pppoe_hdr ) -
PPP_HDRLEN ,
2009-01-22 02:54:54 +03:00
( int __user * ) arg ) )
2005-04-17 02:20:36 +04:00
break ;
err = 0 ;
break ;
case PPPIOCSMRU :
err = - ENXIO ;
if ( ! ( sk - > sk_state & PPPOX_CONNECTED ) )
break ;
err = - EFAULT ;
2009-01-22 02:54:15 +03:00
if ( get_user ( val , ( int __user * ) arg ) )
2005-04-17 02:20:36 +04:00
break ;
if ( val < ( po - > pppoe_dev - > mtu
- sizeof ( struct pppoe_hdr )
- PPP_HDRLEN ) )
err = 0 ;
else
err = - EINVAL ;
break ;
case PPPIOCSFLAGS :
err = - EFAULT ;
2009-01-22 02:54:15 +03:00
if ( get_user ( val , ( int __user * ) arg ) )
2005-04-17 02:20:36 +04:00
break ;
err = 0 ;
break ;
case PPPOEIOCSFWD :
{
struct pppox_sock * relay_po ;
err = - EBUSY ;
if ( sk - > sk_state & ( PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD ) )
break ;
err = - ENOTCONN ;
if ( ! ( sk - > sk_state & PPPOX_CONNECTED ) )
break ;
/* PPPoE address from the user specifies an outbound
2007-03-03 00:16:56 +03:00
PPPoE address which frames are forwarded to */
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
if ( copy_from_user ( & po - > pppoe_relay ,
( void __user * ) arg ,
sizeof ( struct sockaddr_pppox ) ) )
break ;
err = - EINVAL ;
if ( po - > pppoe_relay . sa_family ! = AF_PPPOX | |
2009-01-22 02:54:15 +03:00
po - > pppoe_relay . sa_protocol ! = PX_PROTO_OE )
2005-04-17 02:20:36 +04:00
break ;
/* Check that the socket referenced by the address
actually exists . */
2009-01-22 02:54:54 +03:00
relay_po = get_item_by_addr ( sock_net ( sk ) , & po - > pppoe_relay ) ;
2005-04-17 02:20:36 +04:00
if ( ! relay_po )
break ;
sock_put ( sk_pppox ( relay_po ) ) ;
sk - > sk_state | = PPPOX_RELAY ;
err = 0 ;
break ;
}
case PPPOEIOCDFWD :
err = - EALREADY ;
if ( ! ( sk - > sk_state & PPPOX_RELAY ) )
break ;
sk - > sk_state & = ~ PPPOX_RELAY ;
err = 0 ;
break ;
2007-07-31 04:48:23 +04:00
default :
err = - ENOTTY ;
}
2005-04-17 02:20:36 +04:00
return err ;
}
2006-09-13 21:24:59 +04:00
static int pppoe_sendmsg ( struct kiocb * iocb , struct socket * sock ,
2005-04-17 02:20:36 +04:00
struct msghdr * m , size_t total_len )
{
2007-04-21 03:56:31 +04:00
struct sk_buff * skb ;
2005-04-17 02:20:36 +04:00
struct sock * sk = sock - > sk ;
struct pppox_sock * po = pppox_sk ( sk ) ;
2007-04-21 03:56:31 +04:00
int error ;
2005-04-17 02:20:36 +04:00
struct pppoe_hdr hdr ;
struct pppoe_hdr * ph ;
struct net_device * dev ;
char * start ;
2007-07-31 04:49:13 +04:00
lock_sock ( sk ) ;
2005-04-17 02:20:36 +04:00
if ( sock_flag ( sk , SOCK_DEAD ) | | ! ( sk - > sk_state & PPPOX_CONNECTED ) ) {
error = - ENOTCONN ;
goto end ;
}
hdr . ver = 1 ;
hdr . type = 1 ;
hdr . code = 0 ;
hdr . sid = po - > num ;
dev = po - > pppoe_dev ;
error = - EMSGSIZE ;
2009-01-22 02:54:15 +03:00
if ( total_len > ( dev - > mtu + dev - > hard_header_len ) )
2005-04-17 02:20:36 +04:00
goto end ;
skb = sock_wmalloc ( sk , total_len + dev - > hard_header_len + 32 ,
0 , GFP_KERNEL ) ;
if ( ! skb ) {
error = - ENOMEM ;
goto end ;
}
/* Reserve space for headers. */
skb_reserve ( skb , dev - > hard_header_len ) ;
2007-04-11 07:45:18 +04:00
skb_reset_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
skb - > dev = dev ;
skb - > priority = sk - > sk_priority ;
2009-02-01 11:45:17 +03:00
skb - > protocol = cpu_to_be16 ( ETH_P_PPP_SES ) ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:54 +03:00
ph = ( struct pppoe_hdr * ) skb_put ( skb , total_len + sizeof ( struct pppoe_hdr ) ) ;
start = ( char * ) & ph - > tag [ 0 ] ;
2005-04-17 02:20:36 +04:00
error = memcpy_fromiovec ( start , m - > msg_iov , total_len ) ;
if ( error < 0 ) {
kfree_skb ( skb ) ;
goto end ;
}
error = total_len ;
2007-10-09 12:36:32 +04:00
dev_hard_header ( skb , dev , ETH_P_PPP_SES ,
po - > pppoe_pa . remote , NULL , total_len ) ;
2005-04-17 02:20:36 +04:00
memcpy ( ph , & hdr , sizeof ( struct pppoe_hdr ) ) ;
ph - > length = htons ( total_len ) ;
dev_queue_xmit ( skb ) ;
end :
release_sock ( sk ) ;
return error ;
}
/************************************************************************
*
* xmit function for internal use .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int __pppoe_xmit ( struct sock * sk , struct sk_buff * skb )
{
struct pppox_sock * po = pppox_sk ( sk ) ;
struct net_device * dev = po - > pppoe_dev ;
struct pppoe_hdr * ph ;
int data_len = skb - > len ;
if ( sock_flag ( sk , SOCK_DEAD ) | | ! ( sk - > sk_state & PPPOX_CONNECTED ) )
goto abort ;
if ( ! dev )
goto abort ;
2007-09-17 03:19:50 +04:00
/* Copy the data if there is no space for the header or if it's
* read - only .
*/
2007-09-17 03:21:16 +04:00
if ( skb_cow_head ( skb , sizeof ( * ph ) + dev - > hard_header_len ) )
2007-09-17 03:19:50 +04:00
goto abort ;
2005-04-17 02:20:36 +04:00
2007-09-17 03:20:21 +04:00
__skb_push ( skb , sizeof ( * ph ) ) ;
2007-09-17 03:19:50 +04:00
skb_reset_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
2007-09-17 03:20:21 +04:00
ph = pppoe_hdr ( skb ) ;
ph - > ver = 1 ;
ph - > type = 1 ;
ph - > code = 0 ;
ph - > sid = po - > num ;
ph - > length = htons ( data_len ) ;
2009-02-01 11:45:17 +03:00
skb - > protocol = cpu_to_be16 ( ETH_P_PPP_SES ) ;
2007-09-17 03:19:50 +04:00
skb - > dev = dev ;
2005-04-17 02:20:36 +04:00
2007-10-09 12:36:32 +04:00
dev_hard_header ( skb , dev , ETH_P_PPP_SES ,
po - > pppoe_pa . remote , NULL , data_len ) ;
2005-04-17 02:20:36 +04:00
2007-09-19 21:45:02 +04:00
dev_queue_xmit ( skb ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
abort :
2007-09-17 03:19:50 +04:00
kfree_skb ( skb ) ;
return 1 ;
2005-04-17 02:20:36 +04:00
}
/************************************************************************
*
* xmit function called by generic PPP driver
* sends PPP frame over PPPoE socket
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int pppoe_xmit ( struct ppp_channel * chan , struct sk_buff * skb )
{
2009-01-22 02:54:54 +03:00
struct sock * sk = ( struct sock * ) chan - > private ;
2005-04-17 02:20:36 +04:00
return __pppoe_xmit ( sk , skb ) ;
}
2006-09-13 21:24:59 +04:00
static struct ppp_channel_ops pppoe_chan_ops = {
. start_xmit = pppoe_xmit ,
2005-04-17 02:20:36 +04:00
} ;
static int pppoe_recvmsg ( struct kiocb * iocb , struct socket * sock ,
struct msghdr * m , size_t total_len , int flags )
{
struct sock * sk = sock - > sk ;
2007-04-21 03:56:31 +04:00
struct sk_buff * skb ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
if ( sk - > sk_state & PPPOX_BOUND ) {
error = - EIO ;
goto end ;
}
skb = skb_recv_datagram ( sk , flags & ~ MSG_DONTWAIT ,
flags & MSG_DONTWAIT , & error ) ;
2007-04-21 03:56:31 +04:00
if ( error < 0 )
2005-04-17 02:20:36 +04:00
goto end ;
m - > msg_namelen = 0 ;
if ( skb ) {
2008-06-21 08:58:02 +04:00
total_len = min_t ( size_t , total_len , skb - > len ) ;
2008-06-11 01:07:25 +04:00
error = skb_copy_datagram_iovec ( skb , 0 , m - > msg_iov , total_len ) ;
2007-03-10 21:56:08 +03:00
if ( error = = 0 )
2008-06-11 01:07:25 +04:00
error = total_len ;
2005-04-17 02:20:36 +04:00
}
2007-03-10 21:56:08 +03:00
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
end :
return error ;
}
# ifdef CONFIG_PROC_FS
static int pppoe_seq_show ( struct seq_file * seq , void * v )
{
struct pppox_sock * po ;
char * dev_name ;
if ( v = = SEQ_START_TOKEN ) {
seq_puts ( seq , " Id Address Device \n " ) ;
goto out ;
}
po = v ;
dev_name = po - > pppoe_pa . dev ;
2008-10-28 01:59:26 +03:00
seq_printf ( seq , " %08X %pM %8s \n " ,
2009-01-22 02:54:54 +03:00
po - > pppoe_pa . sid , po - > pppoe_pa . remote , dev_name ) ;
2005-04-17 02:20:36 +04:00
out :
return 0 ;
}
2009-01-22 02:54:54 +03:00
static inline struct pppox_sock * pppoe_get_idx ( struct pppoe_net * pn , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2007-04-21 03:56:31 +04:00
struct pppox_sock * po ;
2009-01-22 02:54:15 +03:00
int i ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:15 +03:00
for ( i = 0 ; i < PPPOE_HASH_SIZE ; i + + ) {
2009-01-22 02:54:54 +03:00
po = pn - > hash_table [ i ] ;
2005-04-17 02:20:36 +04:00
while ( po ) {
if ( ! pos - - )
goto out ;
po = po - > next ;
}
}
2009-01-22 02:54:54 +03:00
2005-04-17 02:20:36 +04:00
out :
return po ;
}
static void * pppoe_seq_start ( struct seq_file * seq , loff_t * pos )
2009-01-22 02:54:54 +03:00
__acquires ( pn - > hash_lock )
2005-04-17 02:20:36 +04:00
{
2009-01-27 08:11:02 +03:00
struct pppoe_net * pn = pppoe_pernet ( seq_file_net ( seq ) ) ;
2005-04-17 02:20:36 +04:00
loff_t l = * pos ;
2009-01-22 02:54:54 +03:00
read_lock_bh ( & pn - > hash_lock ) ;
return l ? pppoe_get_idx ( pn , - - l ) : SEQ_START_TOKEN ;
2005-04-17 02:20:36 +04:00
}
static void * pppoe_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
2009-01-27 08:11:02 +03:00
struct pppoe_net * pn = pppoe_pernet ( seq_file_net ( seq ) ) ;
2005-04-17 02:20:36 +04:00
struct pppox_sock * po ;
+ + * pos ;
if ( v = = SEQ_START_TOKEN ) {
2009-01-22 02:54:54 +03:00
po = pppoe_get_idx ( pn , 0 ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
po = v ;
2006-09-13 21:24:59 +04:00
if ( po - > next )
2005-04-17 02:20:36 +04:00
po = po - > next ;
else {
int hash = hash_item ( po - > pppoe_pa . sid , po - > pppoe_pa . remote ) ;
while ( + + hash < PPPOE_HASH_SIZE ) {
2009-01-22 02:54:54 +03:00
po = pn - > hash_table [ hash ] ;
2005-04-17 02:20:36 +04:00
if ( po )
break ;
}
}
2009-01-22 02:54:54 +03:00
2005-04-17 02:20:36 +04:00
out :
return po ;
}
static void pppoe_seq_stop ( struct seq_file * seq , void * v )
2009-01-22 02:54:54 +03:00
__releases ( pn - > hash_lock )
2005-04-17 02:20:36 +04:00
{
2009-01-27 08:11:02 +03:00
struct pppoe_net * pn = pppoe_pernet ( seq_file_net ( seq ) ) ;
2009-01-22 02:54:54 +03:00
read_unlock_bh ( & pn - > hash_lock ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-15 00:52:18 +03:00
static const struct seq_operations pppoe_seq_ops = {
2005-04-17 02:20:36 +04:00
. start = pppoe_seq_start ,
. next = pppoe_seq_next ,
. stop = pppoe_seq_stop ,
. show = pppoe_seq_show ,
} ;
static int pppoe_seq_open ( struct inode * inode , struct file * file )
{
2009-01-27 08:11:02 +03:00
return seq_open_net ( inode , file , & pppoe_seq_ops ,
sizeof ( struct seq_net_private ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:34 +03:00
static const struct file_operations pppoe_seq_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = pppoe_seq_open ,
. read = seq_read ,
. llseek = seq_lseek ,
2009-01-27 08:11:02 +03:00
. release = seq_release_net ,
2005-04-17 02:20:36 +04:00
} ;
# endif /* CONFIG_PROC_FS */
2005-12-28 07:57:40 +03:00
static const struct proto_ops pppoe_ops = {
2009-01-22 02:54:15 +03:00
. family = AF_PPPOX ,
. owner = THIS_MODULE ,
. release = pppoe_release ,
. bind = sock_no_bind ,
. connect = pppoe_connect ,
. socketpair = sock_no_socketpair ,
. accept = sock_no_accept ,
. getname = pppoe_getname ,
. poll = datagram_poll ,
. listen = sock_no_listen ,
. shutdown = sock_no_shutdown ,
. setsockopt = sock_no_setsockopt ,
. getsockopt = sock_no_getsockopt ,
. sendmsg = pppoe_sendmsg ,
. recvmsg = pppoe_recvmsg ,
. mmap = sock_no_mmap ,
. ioctl = pppox_ioctl ,
2005-04-17 02:20:36 +04:00
} ;
static struct pppox_proto pppoe_proto = {
2009-01-22 02:54:15 +03:00
. create = pppoe_create ,
. ioctl = pppoe_ioctl ,
. owner = THIS_MODULE ,
2005-04-17 02:20:36 +04:00
} ;
2009-01-22 02:54:54 +03:00
static __net_init int pppoe_init_net ( struct net * net )
{
struct pppoe_net * pn ;
struct proc_dir_entry * pde ;
int err ;
pn = kzalloc ( sizeof ( * pn ) , GFP_KERNEL ) ;
if ( ! pn )
return - ENOMEM ;
rwlock_init ( & pn - > hash_lock ) ;
err = net_assign_generic ( net , pppoe_net_id , pn ) ;
if ( err )
goto out ;
pde = proc_net_fops_create ( net , " pppoe " , S_IRUGO , & pppoe_seq_fops ) ;
# ifdef CONFIG_PROC_FS
if ( ! pde ) {
err = - ENOMEM ;
goto out ;
}
# endif
return 0 ;
out :
kfree ( pn ) ;
return err ;
}
static __net_exit void pppoe_exit_net ( struct net * net )
{
struct pppoe_net * pn ;
proc_net_remove ( net , " pppoe " ) ;
pn = net_generic ( net , pppoe_net_id ) ;
/*
* if someone has cached our net then
* further net_generic call will return NULL
*/
net_assign_generic ( net , pppoe_net_id , NULL ) ;
kfree ( pn ) ;
}
2009-02-10 05:05:16 +03:00
static struct pernet_operations pppoe_net_ops = {
2009-01-22 02:54:54 +03:00
. init = pppoe_init_net ,
. exit = pppoe_exit_net ,
} ;
2005-04-17 02:20:36 +04:00
static int __init pppoe_init ( void )
{
2009-01-22 02:54:54 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2009-01-22 02:54:54 +03:00
err = proto_register ( & pppoe_sk_proto , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto out ;
2009-01-22 02:54:15 +03:00
err = register_pppox_proto ( PX_PROTO_OE , & pppoe_proto ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto out_unregister_pppoe_proto ;
2009-01-22 02:54:54 +03:00
err = register_pernet_gen_device ( & pppoe_net_id , & pppoe_net_ops ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto out_unregister_pppox_proto ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
dev_add_pack ( & pppoes_ptype ) ;
dev_add_pack ( & pppoed_ptype ) ;
register_netdevice_notifier ( & pppoe_notifier ) ;
2009-01-22 02:54:54 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
out_unregister_pppox_proto :
unregister_pppox_proto ( PX_PROTO_OE ) ;
out_unregister_pppoe_proto :
proto_unregister ( & pppoe_sk_proto ) ;
2009-01-22 02:54:54 +03:00
out :
return err ;
2005-04-17 02:20:36 +04:00
}
static void __exit pppoe_exit ( void )
{
unregister_pppox_proto ( PX_PROTO_OE ) ;
dev_remove_pack ( & pppoes_ptype ) ;
dev_remove_pack ( & pppoed_ptype ) ;
unregister_netdevice_notifier ( & pppoe_notifier ) ;
2009-01-22 02:54:54 +03:00
unregister_pernet_gen_device ( pppoe_net_id , & pppoe_net_ops ) ;
2005-04-17 02:20:36 +04:00
proto_unregister ( & pppoe_sk_proto ) ;
}
module_init ( pppoe_init ) ;
module_exit ( pppoe_exit ) ;
MODULE_AUTHOR ( " Michal Ostrowski <mostrows@speakeasy.net> " ) ;
MODULE_DESCRIPTION ( " PPP over Ethernet driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_NETPROTO ( PF_PPPOX ) ;