OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 199 to Rev 200
    Reverse comparison

Rev 199 → Rev 200

/trunk/uclinux/uClinux-2.0.x/net/core/sysctl_net_core.c
0,0 → 1,23
/* -*- linux-c -*-
* sysctl_net_core.c: sysctl interface to net core subsystem.
*
* Begun April 1, 1996, Mike Shaver.
* Added /proc/sys/net/core directory entry (empty =) ). [MS]
*/
 
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <linux/config.h>
 
#ifdef CONFIG_NET_ALIAS
extern int sysctl_net_alias_max;
extern int proc_do_net_alias_max(ctl_table *, int, struct file *, void *, size_t *);
#endif
 
ctl_table core_table[] = {
#ifdef CONFIG_NET_ALIAS
{NET_CORE_NET_ALIAS_MAX, "net_alias_max", &sysctl_net_alias_max, sizeof(int),
0644, NULL, &proc_do_net_alias_max },
#endif
{0}
};
/trunk/uclinux/uClinux-2.0.x/net/core/sock.c
0,0 → 1,601
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* Generic socket support routines. Memory allocators, socket lock/release
* handler for protocols to use and generic option handler.
*
*
* Version: @(#)sock.c 1.0.17 06/02/93
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Florian La Roche, <flla@stud.uni-sb.de>
* Alan Cox, <A.Cox@swansea.ac.uk>
*
* Fixes:
* Alan Cox : Numerous verify_area() problems
* Alan Cox : Connecting on a connecting socket
* now returns an error for tcp.
* Alan Cox : sock->protocol is set correctly.
* and is not sometimes left as 0.
* Alan Cox : connect handles icmp errors on a
* connect properly. Unfortunately there
* is a restart syscall nasty there. I
* can't match BSD without hacking the C
* library. Ideas urgently sought!
* Alan Cox : Disallow bind() to addresses that are
* not ours - especially broadcast ones!!
* Alan Cox : Socket 1024 _IS_ ok for users. (fencepost)
* Alan Cox : sock_wfree/sock_rfree don't destroy sockets,
* instead they leave that for the DESTROY timer.
* Alan Cox : Clean up error flag in accept
* Alan Cox : TCP ack handling is buggy, the DESTROY timer
* was buggy. Put a remove_sock() in the handler
* for memory when we hit 0. Also altered the timer
* code. The ACK stuff can wait and needs major
* TCP layer surgery.
* Alan Cox : Fixed TCP ack bug, removed remove sock
* and fixed timer/inet_bh race.
* Alan Cox : Added zapped flag for TCP
* Alan Cox : Move kfree_skb into skbuff.c and tidied up surplus code
* Alan Cox : for new sk_buff allocations wmalloc/rmalloc now call alloc_skb
* Alan Cox : kfree_s calls now are kfree_skbmem so we can track skb resources
* Alan Cox : Supports socket option broadcast now as does udp. Packet and raw need fixing.
* Alan Cox : Added RCVBUF,SNDBUF size setting. It suddenly occurred to me how easy it was so...
* Rick Sladkey : Relaxed UDP rules for matching packets.
* C.E.Hawkins : IFF_PROMISC/SIOCGHWADDR support
* Pauline Middelink : identd support
* Alan Cox : Fixed connect() taking signals I think.
* Alan Cox : SO_LINGER supported
* Alan Cox : Error reporting fixes
* Anonymous : inet_create tidied up (sk->reuse setting)
* Alan Cox : inet sockets don't set sk->type!
* Alan Cox : Split socket option code
* Alan Cox : Callbacks
* Alan Cox : Nagle flag for Charles & Johannes stuff
* Alex : Removed restriction on inet fioctl
* Alan Cox : Splitting INET from NET core
* Alan Cox : Fixed bogus SO_TYPE handling in getsockopt()
* Adam Caldwell : Missing return in SO_DONTROUTE/SO_DEBUG code
* Alan Cox : Split IP from generic code
* Alan Cox : New kfree_skbmem()
* Alan Cox : Make SO_DEBUG superuser only.
* Alan Cox : Allow anyone to clear SO_DEBUG
* (compatibility fix)
* Alan Cox : Added optimistic memory grabbing for AF_UNIX throughput.
* Alan Cox : Allocator for a socket is settable.
* Alan Cox : SO_ERROR includes soft errors.
* Alan Cox : Allow NULL arguments on some SO_ opts
* Alan Cox : Generic socket allocation to make hooks
* easier (suggested by Craig Metz).
* Michael Pall : SO_ERROR returns positive errno again
* Elliot Poger : Added support for SO_BINDTODEVICE.
* Russell King : Add #ifdef CONFIG_INET to SO_BINDTODEVICE
*
* To Fix:
*
*
* 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/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
 
#include <asm/segment.h>
#include <asm/system.h>
 
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/arp.h>
#include <net/rarp.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/raw.h>
#include <net/icmp.h>
 
#define min(a,b) ((a)<(b)?(a):(b))
 
/*
* This is meant for all protocols to use and covers goings on
* at the socket level. Everything here is generic.
*/
 
int sock_setsockopt(struct sock *sk, int level, int optname,
char *optval, int optlen)
{
int val;
int valbool;
int err;
struct linger ling;
struct ifreq req;
 
/*
* Options without arguments
*/
 
#ifdef SO_DONTLINGER /* Compatibility item... */
switch(optname)
{
case SO_DONTLINGER:
sk->linger=0;
return 0;
}
#endif
if (optval == NULL)
return(-EINVAL);
 
err=verify_area(VERIFY_READ, optval, sizeof(int));
if(err)
return err;
val = get_user((int *)optval);
valbool = val?1:0;
switch(optname)
{
case SO_DEBUG:
if(val && !suser())
return(-EPERM);
sk->debug=valbool;
return 0;
case SO_REUSEADDR:
sk->reuse = valbool;
return(0);
case SO_TYPE:
case SO_ERROR:
return(-ENOPROTOOPT);
case SO_DONTROUTE:
sk->localroute=valbool;
return 0;
case SO_BROADCAST:
sk->broadcast=valbool;
return 0;
case SO_SNDBUF:
if(val > SK_WMEM_MAX*2)
val = SK_WMEM_MAX*2;
if(val < 256)
val = 256;
if(val > 65535)
val = 65535;
sk->sndbuf = val;
return 0;
 
case SO_RCVBUF:
if(val > SK_RMEM_MAX*2)
val = SK_RMEM_MAX*2;
if(val < 256)
val = 256;
if(val > 65535)
val = 65535;
sk->rcvbuf = val;
return(0);
 
case SO_KEEPALIVE:
sk->keepopen = valbool;
return(0);
 
case SO_OOBINLINE:
sk->urginline = valbool;
return(0);
 
case SO_NO_CHECK:
sk->no_check = valbool;
return(0);
 
case SO_PRIORITY:
if (val >= 0 && val < DEV_NUMBUFFS)
{
sk->priority = val;
}
else
{
return(-EINVAL);
}
return(0);
 
 
case SO_LINGER:
err=verify_area(VERIFY_READ,optval,sizeof(ling));
if(err)
return err;
memcpy_fromfs(&ling,optval,sizeof(ling));
if(ling.l_onoff==0)
sk->linger=0;
else
{
sk->lingertime=ling.l_linger;
sk->linger=1;
}
return 0;
 
case SO_BSDCOMPAT:
sk->bsdism = valbool;
return 0;
 
#ifdef CONFIG_NET
case SO_BINDTODEVICE:
/* Bind this socket to a particular device like "eth0",
* as specified in an ifreq structure. If the device
* is "", socket is NOT bound to a device. */
if(!suser())
return -EPERM;
if (!valbool) {
sk->bound_device = NULL;
} else {
err=verify_area(VERIFY_READ,optval,sizeof(req));
if(err)
return err;
memcpy_fromfs(&req,optval,sizeof(req));
#ifdef CONFIG_INET
/* Remove any cached route for this socket. */
if (sk->ip_route_cache) {
ip_rt_put(sk->ip_route_cache);
sk->ip_route_cache=NULL;
}
#endif
if (*(req.ifr_name) == '\0') {
sk->bound_device = NULL;
} else {
sk->bound_device = dev_get(req.ifr_name);
if (sk->bound_device == NULL)
return -EINVAL;
}
}
return 0;
#endif
 
default:
return(-ENOPROTOOPT);
}
}
 
 
int sock_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
int val;
int err;
struct linger ling;
 
switch(optname)
{
case SO_DEBUG:
val = sk->debug;
break;
case SO_DONTROUTE:
val = sk->localroute;
break;
case SO_BROADCAST:
val= sk->broadcast;
break;
 
case SO_SNDBUF:
val=sk->sndbuf;
break;
case SO_RCVBUF:
val =sk->rcvbuf;
break;
 
case SO_REUSEADDR:
val = sk->reuse;
break;
 
case SO_KEEPALIVE:
val = sk->keepopen;
break;
 
case SO_TYPE:
val = sk->type;
break;
 
case SO_ERROR:
val = -sock_error(sk);
if(val==0)
val=xchg(&sk->err_soft,0);
break;
 
case SO_OOBINLINE:
val = sk->urginline;
break;
case SO_NO_CHECK:
val = sk->no_check;
break;
 
case SO_PRIORITY:
val = sk->priority;
break;
case SO_LINGER:
err=verify_area(VERIFY_WRITE,optval,sizeof(ling));
if(err)
return err;
err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
if(err)
return err;
put_fs_long(sizeof(ling),(unsigned long *)optlen);
ling.l_onoff=sk->linger;
ling.l_linger=sk->lingertime;
memcpy_tofs(optval,&ling,sizeof(ling));
return 0;
case SO_BSDCOMPAT:
val = sk->bsdism;
break;
 
#ifdef CONFIG_NET
case SO_BINDTODEVICE:
{
struct ifreq req;
 
/* Return the bound device (if any) */
err=verify_area(VERIFY_WRITE,optval,sizeof(req));
if(err)
return err;
 
memset((char *) &req, 0, sizeof(req));
 
if (sk->bound_device) {
strncpy(req.ifr_name, sk->bound_device->name, sizeof(req.ifr_name));
(*(struct sockaddr_in *) &req.ifr_addr).sin_family = sk->bound_device->family;
(*(struct sockaddr_in *) &req.ifr_addr).sin_addr.s_addr = sk->bound_device->pa_addr;
}
memcpy_tofs(optval, &req, sizeof(req));
return 0;
}
#endif
 
default:
return(-ENOPROTOOPT);
}
err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
if(err)
return err;
put_fs_long(sizeof(int),(unsigned long *) optlen);
 
err=verify_area(VERIFY_WRITE, optval, sizeof(int));
if(err)
return err;
put_fs_long(val,(unsigned long *)optval);
 
return(0);
}
 
struct sock *sk_alloc(int priority)
{
struct sock *sk=(struct sock *)kmalloc(sizeof(*sk), priority);
if(!sk)
return NULL;
memset(sk, 0, sizeof(*sk));
return sk;
}
 
void sk_free(struct sock *sk)
{
kfree_s(sk,sizeof(*sk));
}
 
 
struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority)
{
if (sk) {
if (force || sk->wmem_alloc < sk->sndbuf) {
struct sk_buff * skb = alloc_skb(size, priority);
if (skb)
atomic_add(skb->truesize, &sk->wmem_alloc);
return skb;
}
return NULL;
}
return alloc_skb(size, priority);
}
 
struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority)
{
if (sk) {
if (force || sk->rmem_alloc < sk->rcvbuf) {
struct sk_buff *skb = alloc_skb(size, priority);
if (skb)
atomic_add(skb->truesize, &sk->rmem_alloc);
return skb;
}
return NULL;
}
return alloc_skb(size, priority);
}
 
 
unsigned long sock_rspace(struct sock *sk)
{
int amt;
 
if (sk != NULL)
{
if (sk->rmem_alloc >= sk->rcvbuf-2*MIN_WINDOW)
return(0);
amt = min((sk->rcvbuf-sk->rmem_alloc)/2-MIN_WINDOW, MAX_WINDOW);
if (amt < 0)
return(0);
return(amt);
}
return(0);
}
 
 
unsigned long sock_wspace(struct sock *sk)
{
if (sk != NULL)
{
if (sk->shutdown & SEND_SHUTDOWN)
return(0);
if (sk->wmem_alloc >= sk->sndbuf)
return(0);
return sk->sndbuf - sk->wmem_alloc;
}
return(0);
}
 
 
void sock_wfree(struct sock *sk, struct sk_buff *skb)
{
int s=skb->truesize;
#if CONFIG_SKB_CHECK
IS_SKB(skb);
#endif
kfree_skbmem(skb);
if (sk)
{
/* In case it might be waiting for more memory. */
sk->write_space(sk);
atomic_sub(s, &sk->wmem_alloc);
}
}
 
 
void sock_rfree(struct sock *sk, struct sk_buff *skb)
{
int s=skb->truesize;
#if CONFIG_SKB_CHECK
IS_SKB(skb);
#endif
kfree_skbmem(skb);
if (sk)
{
atomic_sub(s, &sk->rmem_alloc);
}
}
 
/*
* Generic send/receive buffer handlers
*/
 
struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigned long fallback, int noblock, int *errcode)
{
struct sk_buff *skb;
int err;
unsigned long mem;
do
{
if(sk->err!=0)
{
cli();
err= -sk->err;
sk->err=0;
sti();
*errcode=err;
return NULL;
}
if(sk->shutdown&SEND_SHUTDOWN)
{
*errcode=-EPIPE;
return NULL;
}
 
mem=sk->wmem_alloc;
 
if(!fallback)
skb = sock_wmalloc(sk, size, 0, sk->allocation);
else
{
/* The buffer get won't block, or use the atomic queue. It does
produce annoying no free page messages still.... */
skb = sock_wmalloc(sk, size, 0 , GFP_IO);
if(!skb)
skb=sock_wmalloc(sk, fallback, 0, GFP_KERNEL);
}
/*
* This means we have too many buffers for this socket already.
*/
if(skb==NULL)
{
sk->socket->flags |= SO_NOSPACE;
if(noblock)
{
*errcode=-EAGAIN;
return NULL;
}
if(sk->shutdown&SEND_SHUTDOWN)
{
*errcode=-EPIPE;
return NULL;
}
cli();
if(sk->shutdown&SEND_SHUTDOWN)
{
sti();
*errcode=-EPIPE;
return NULL;
}
if (sk->wmem_alloc==mem)
{
sk->socket->flags &= ~SO_NOSPACE;
interruptible_sleep_on(sk->sleep);
if (current->signal & ~current->blocked)
{
sti();
*errcode = -ERESTARTSYS;
return NULL;
}
}
sti();
}
}
while(skb==NULL);
return skb;
}
 
 
void __release_sock(struct sock *sk)
{
#ifdef CONFIG_INET
if (!sk->prot || !sk->prot->rcv)
return;
/* See if we have any packets built up. */
start_bh_atomic();
while (!skb_queue_empty(&sk->back_log)) {
struct sk_buff * skb = sk->back_log.next;
__skb_unlink(skb, &sk->back_log);
sk->prot->rcv(skb, skb->dev, (struct options*)skb->proto_priv,
skb->saddr, skb->len, skb->daddr, 1,
/* Only used for/by raw sockets. */
(struct inet_protocol *)sk->pair);
}
end_bh_atomic();
#endif
}
/trunk/uclinux/uClinux-2.0.x/net/core/net_alias.c
0,0 → 1,1510
/*
* NET_ALIAS network device aliasing module.
*
*
* Version: @(#)net_alias.c 0.50 4/20/97
*
*
* Authors: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
* Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar>
*
* Features:
* - AF_ independent: net_alias_type objects
* - AF_INET optimized
* - ACTUAL alias devices inserted in dev chain
* - fast hashed alias address lookup
* - net_alias_type objs registration/unreg., module-ables.
* - /proc/net/aliases & /proc/net/alias_types entries
* - tx and rx stats
* - /proc/sys/net/core/net_alias_max entry
* Fixes:
* Juan Jose Ciarlante : several net_alias_type func. renamed.
* Juan Jose Ciarlante : net_alias_type object methods now pass
* *this.
* Juan Jose Ciarlante : xxx_rcv device selection based on <src,dst> addrs
* Andreas Schultz : Kerneld support.
* Juan Jose Ciarlante : Added tx/rx stats for aliases.
* Juan Jose Ciarlante : Added sysctl interface for max aliases per device
*
* FIXME:
* - User calls sleep/wake_up locking.
*
*
* 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/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/sysctl.h>
 
#ifdef ALIAS_USER_LAND_DEBUG
#include "net_alias.h"
#include "user_stubs.h"
#endif
 
#include <linux/net_alias.h>
 
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif
 
 
/*
* NET_ALIAS_MAX_DEFAULT: max. alias slot number allowed by default,
* can be changed by sysctl
* NET_ALIAS_HASH_TAB_SIZE: hash table size (addr lookup), 1 per aliased
* device, due to hash optimizations, MUST be 16 or 256 only.
*/
 
#define NET_ALIAS_MAX_DEFAULT 256
 
/* DO NOT CHANGE the line below ! */
#define NET_ALIAS_HASH_TAB_SIZE(n) ( ((n)>=NET_ALIAS_MAX_DEFAULT) ? 256 : 16 )
 
/*
* set default max_aliases per device
*/
int sysctl_net_alias_max = NET_ALIAS_MAX_DEFAULT;
 
/*
* Only allow the following flags to pass from main device to aliases
* Note that IFF_BROADCAST is not passed by default, this make sense
* because:
* a) if same-net alias: broadcasts are already handled by main device
* b) if diff-net alias: bcasts will be set by 'broadcast' ifconfig opt.
* I prefer this approach instead of setting '-broadcast' for each
* same-net alias device --JJC.
*/
 
#define NET_ALIAS_IFF_MASK (IFF_SOFTHEADERS|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
 
static struct net_alias_type * nat_getbytype(int type);
static int nat_attach_chg(struct net_alias_type *nat, int delta);
static int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa);
static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias);
 
 
static int net_alias_devinit(struct device *dev);
static struct enet_statistics *net_alias_dev_stats(struct device *dev);
static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev);
static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa);
static struct net_alias **net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias);
static struct device *net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data);
static struct device *net_alias_dev_delete(struct device *main_dev, int slot, int *err);
static void net_alias_free(struct device *dev);
/*
* net_alias_type base array, will hold net_alias_type obj hashed list heads.
*/
 
struct net_alias_type *net_alias_type_base[16];
 
 
/*
* get_stats function: return rx/tx pkts based on lookups
*/
static struct enet_statistics *net_alias_dev_stats(struct device *dev)
{
static struct enet_statistics alias_stats;
struct net_alias *alias = dev->my_alias;
memset (&alias_stats, 0, sizeof (alias_stats));
alias_stats.rx_packets = alias->rx_lookups;
alias_stats.tx_packets = alias->tx_lookups;
return &alias_stats;
}
 
 
/*
* get net_alias_type ptr by type
*/
 
static __inline__ struct net_alias_type *
nat_getbytype(int type)
{
struct net_alias_type *nat;
for(nat = net_alias_type_base[type & 0x0f]; nat ; nat = nat->next)
{
if (nat->type == type) return nat;
}
return NULL;
}
 
 
/*
* get addr32 representation (pre-hashing) of address.
* if NULL nat->get_addr32, assume sockaddr_in struct (IP-ish).
*/
 
static __inline__ __u32
nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
{
if (nat->get_addr32)
return nat->get_addr32(nat, sa);
else
return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
}
 
 
/*
* hashing code for alias_info->hash_tab entries
* 4 bytes -> 1/2 byte using xor complemented by af
*/
 
static __inline__ unsigned
hash_key(unsigned hsize, __u32 addr)
{
unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
tmp ^= (tmp>>8); /* 2 -> 1 */
if (hsize == 256)
return (tmp & 0xff);
else
return (tmp^(tmp>>4)) & 0x0f; /* 1 -> 1/2 */
}
 
 
/*
* get hash key for supplied net alias type and address
* nat must be !NULL
* the purpose here is to map a net_alias_type and a generic
* address to a hash code.
*/
 
static __inline__ int
nat_hash_key(struct net_alias_type *nat, unsigned hsize, struct sockaddr *sa)
{
return hash_key(hsize, nat_addr32(nat,sa));
}
 
 
/*
* change net_alias_type number of attachments (bindings)
*/
 
static int
nat_attach_chg(struct net_alias_type *nat, int delta)
{
unsigned long flags;
int n_at;
if (!nat) return -1;
save_flags(flags);
cli();
n_at = nat->n_attach + delta;
if (n_at < 0)
{
restore_flags(flags);
printk(KERN_WARNING "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n",
nat->type);
return -1;
}
nat->n_attach = n_at;
restore_flags(flags);
return 0;
}
 
 
/*
* bind alias to its type (family) object and call initialization hook
*/
 
static __inline__ int
nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa)
{
if (nat->alias_init_1) nat->alias_init_1(nat, alias, sa);
return nat_attach_chg(nat, +1);
}
 
 
/*
* unbind alias from type object and call alias destructor
*/
 
static __inline__ int
nat_unbind(struct net_alias_type *nat, struct net_alias *alias)
{
if (nat->alias_done_1) nat->alias_done_1(nat, alias);
return nat_attach_chg(nat, -1);
}
 
 
/*
* compare device address with given. if NULL nat->dev_addr_chk,
* compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish)
*/
 
static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat,
struct device *dev, struct sockaddr *sa)
{
if (nat->dev_addr_chk)
return nat->dev_addr_chk(nat, dev, sa);
else
return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr);
}
 
 
/*
* alias device init()
* do nothing.
*/
 
static int
net_alias_devinit(struct device *dev)
{
#ifdef ALIAS_USER_LAND_DEBUG
printk("net_alias_devinit(%s) called.\n", dev->name);
#endif
return 0;
}
 
 
/*
* hard_start_xmit() should not be called.
* ignore ... but shout!.
*/
 
static int
net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev)
{
printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name);
dev_kfree_skb(skb, FREE_WRITE);
return 0;
}
 
 
static int
net_alias_dev_open(struct device * dev)
{
return 0;
}
 
static int
net_alias_dev_close(struct device * dev)
{
return 0;
}
 
/*
* setups a new (alias) device
*/
 
static int
net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat,
struct sockaddr *sa)
{
struct device *main_dev;
struct device *dev;
int family;
int i;
 
/*
*
* generic device setup based on main_dev info
*
* FIXME: is NULL bitwise 0 for all Linux platforms?
*/
main_dev = alias->main_dev;
dev = &alias->dev;
memset(dev, '\0', sizeof(struct device));
family = (sa)? sa->sa_family : main_dev->family;
alias->rx_lookups = 0;
alias->tx_lookups = 0;
dev->alias_info = NULL; /* no aliasing recursion */
dev->my_alias = alias; /* point to alias */
dev->name = alias->name;
dev->type = main_dev->type;
dev->open = net_alias_dev_open;
dev->stop = net_alias_dev_close;
dev->get_stats = net_alias_dev_stats;
dev->hard_header_len = main_dev->hard_header_len;
memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
dev->addr_len = main_dev->addr_len;
dev->init = net_alias_devinit;
dev->hard_start_xmit = net_alias_hard_start_xmit;
dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP;
 
/*
* only makes sense if same family
*/
if (family == main_dev->family)
{
dev->metric = main_dev->metric;
dev->mtu = main_dev->mtu;
dev->pa_alen = main_dev->pa_alen;
dev->hard_header = main_dev->hard_header;
dev->rebuild_header = main_dev->rebuild_header;
}
/*
* Fill in the generic fields of the device structure.
* not actually used, avoids some dev.c #ifdef's
*/
for (i = 0; i < DEV_NUMBUFFS; i++)
skb_queue_head_init(&dev->buffs[i]);
dev->family = family;
return 0;
}
 
 
/*
* slow alias find (parse the whole hash_tab)
* returns: alias' pointer address
*/
 
static struct net_alias **
net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias)
{
unsigned idx, n_aliases;
struct net_alias **aliasp;
 
/*
* for each alias_info's hash_tab entry, for every alias ...
*/
n_aliases = alias_info->n_aliases;
for (idx=0; idx < alias_info->hash_tab_size ; idx++)
for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next)
if (*aliasp == alias)
return aliasp;
else
if (--n_aliases == 0) break; /* faster give up */
return NULL;
}
 
 
/*
* create alias device for main_dev with given slot num.
* if sa==NULL will create a same_family alias device
*/
 
static struct device *
net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data)
{
struct net_alias_info *alias_info;
struct net_alias *alias, **aliasp;
struct net_alias_type *nat;
struct device *dev;
unsigned long flags;
int family;
__u32 addr32;
int max_aliases;
/* FIXME: lock */
alias_info = main_dev->alias_info;
 
/*
* if NULL address given, take family from main_dev
*/
family = (sa)? sa->sa_family : main_dev->family;
/*
* check if wanted family has a net_alias_type object registered
*/
nat = nat_getbytype(family);
if (!nat) {
#ifdef CONFIG_KERNELD
char modname[20];
sprintf (modname,"netalias-%d", family);
request_module(modname);
 
nat = nat_getbytype(family);
if (!nat) {
#endif
printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n",
main_dev->name, slot, family);
/* *err = -EAFNOSUPPORT; */
*err = -EINVAL;
return NULL;
#ifdef CONFIG_KERNELD
}
#endif
}
*err = -EINVAL;
if (!alias_info)
/*
* At this point we take the sysctl value(s)
*/
max_aliases = sysctl_net_alias_max;
else
max_aliases = alias_info->max_aliases;
if (slot >= max_aliases ) /* 0-based (eth0:0 _IS_ valid) */
return NULL;
/*
* do not allow creation over downed devices
*/
 
*err = -EIO;
if (! (main_dev->flags & IFF_UP) )
return NULL;
/*
* if first alias, must also create alias_info
*/
*err = -ENOMEM;
 
if (!alias_info)
{
/*
* Allocate space for struct net_alias_info plus hash table
*/
int truesize;
 
/* net_alias_info size */
truesize = sizeof(struct net_alias_info);
/* add hash_tab size * sizeof(elem) */
truesize += NET_ALIAS_HASH_TAB_SIZE(max_aliases) * sizeof (struct net_alias *);
alias_info = kmalloc( truesize , GFP_KERNEL);
if (!alias_info) return NULL; /* ENOMEM */
memset(alias_info, 0, truesize);
alias_info->truesize = truesize;
alias_info->max_aliases = max_aliases;
alias_info->hash_tab_size = NET_ALIAS_HASH_TAB_SIZE(max_aliases);
}
if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
return NULL; /* ENOMEM */
 
/*
* FIXME: is NULL bitwise 0 for all Linux platforms?
*/
memset(alias, 0, sizeof(struct net_alias));
alias->slot = slot;
alias->main_dev = main_dev;
alias->nat = nat;
alias->next = NULL;
alias->data = data;
sprintf(alias->name, "%s:%d", main_dev->name, slot);
/*
* initialise alias' device structure
*/
net_alias_devsetup(alias, nat, sa);
 
dev = &alias->dev;
save_flags(flags);
cli();
 
/*
* bind alias to its object type
* nat_bind calls nat->alias_init_1
*/
nat_bind(nat, alias, sa);
 
/*
* if no address passed, take from device (could have been
* set by nat->alias_init_1)
*/
addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr;
/*
* store hash key in alias: will speed-up rehashing and deletion
*/
alias->hash = hash_key(alias_info->hash_tab_size, addr32);
 
/*
* insert alias in hashed linked list
*/
aliasp = &alias_info->hash_tab[alias->hash];
alias->next = *aliasp;
*aliasp = alias;
 
/*
* if first alias ...
*/
if (!alias_info->n_aliases++)
{
alias_info->taildev = main_dev;
main_dev->alias_info = alias_info;
}
/*
* add device at tail (just after last main_dev alias)
*/
dev->next = alias_info->taildev->next;
alias_info->taildev->next = dev;
alias_info->taildev = dev;
restore_flags(flags);
return dev;
}
 
 
/*
* delete one main_dev alias (referred by its slot num)
*/
 
static struct device *
net_alias_dev_delete(struct device *main_dev, int slot, int *err)
{
struct net_alias_info *alias_info;
struct net_alias *alias, **aliasp;
struct device *dev;
unsigned n_aliases;
unsigned long flags;
struct net_alias_type *nat;
struct device *prevdev;
/* FIXME: lock */
*err = -ENODEV;
if (main_dev == NULL) return NULL;
 
/*
* does main_dev have aliases?
*/
alias_info = main_dev->alias_info;
if (!alias_info) return NULL; /* ENODEV */
n_aliases = alias_info->n_aliases;
/*
* find device that holds the same slot number (could also
* be strcmp() ala dev_get).
*/
for (prevdev=main_dev, alias = NULL;prevdev->next && n_aliases; prevdev = prevdev->next)
{
if (!(alias = prevdev->next->my_alias))
{
printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n");
continue; /* or should give up? */
}
if (alias->slot == slot) break;
alias = NULL;
n_aliases--;
}
if (!alias) return NULL; /* ENODEV */
dev = &alias->dev;
/*
* find alias hashed entry
*/
for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
if(*aliasp == alias) break;
 
/*
* if not found (???), try a full search
*/
if (*aliasp != alias)
if ((aliasp = net_alias_slow_findp(alias_info, alias)))
printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name);
else
{
printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name);
return NULL; /* ENODEV */
}
nat = alias->nat;
 
save_flags(flags);
cli();
/*
* unbind alias from alias_type obj.
*/
nat_unbind(nat, alias);
/*
* is alias at tail?
*/
if ( dev == alias_info->taildev )
alias_info->taildev = prevdev;
/*
* unlink and close device
*/
prevdev->next = dev->next;
dev_close(dev);
/*
* unlink alias
*/
*aliasp = (*aliasp)->next;
 
if (--alias_info->n_aliases == 0) /* last alias */
main_dev->alias_info = NULL;
restore_flags(flags);
 
/*
* now free structures
*/
kfree_s(alias, sizeof(struct net_alias));
if (main_dev->alias_info == NULL)
kfree_s(alias_info, alias_info->truesize);
 
/*
* deletion ok (*err=0), NULL device returned.
*/
*err = 0;
return NULL;
}
 
/*
* free all main device aliasing stuff
* will be called on dev_close(main_dev)
*/
 
static void
net_alias_free(struct device *main_dev)
{
struct net_alias_info *alias_info;
struct net_alias *alias;
struct net_alias_type *nat;
struct device *dev;
unsigned long flags;
 
/*
* do I really have aliases?
*/
if (!(alias_info = main_dev->alias_info)) return;
 
/*
* fast device link "short-circuit": set main_dev->next to
* device after last alias
*/
save_flags(flags);
cli();
dev = main_dev->next;
main_dev->next = alias_info->taildev->next;
main_dev->alias_info = NULL;
alias_info->taildev->next = NULL;
restore_flags(flags);
 
/*
* loop over alias devices, free and dev_close()
*/
while (dev)
{
if (net_alias_is(dev))
{
alias = dev->my_alias;
if (alias->main_dev == main_dev)
{
/*
* unbind alias from alias_type object
*/
nat = alias->nat;
if (nat)
{
nat_unbind(nat, alias);
} /* else error/printk ??? */
dev_close(dev);
dev = dev->next;
kfree_s(alias, sizeof(struct net_alias));
continue;
}
else
printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n",
main_dev->name, alias->name);
}
else
printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n",
main_dev->name);
dev = dev->next;
}
kfree_s(alias_info, sizeof(alias_info));
return;
}
 
/*
* dev_get() with added alias naming magic.
*/
 
struct device *
net_alias_dev_get(char *dev_name, int aliasing_ok, int *err,
struct sockaddr *sa, void *data)
{
struct device *dev;
char *sptr,*eptr;
int slot = 0;
int delete = 0;
*err = -ENODEV;
if ((dev=dev_get(dev_name)))
return dev;
 
/*
* want alias naming magic?
*/
if (!aliasing_ok) return NULL;
 
if (!dev_name || !*dev_name)
return NULL;
/*
* find the first ':' , must be followed by, at least, 1 char
*/
for (sptr=dev_name ; *sptr ; sptr++) if(*sptr==':') break;
if (!*sptr || !*(sptr+1))
return NULL;
/*
* seems to be an alias name, fetch main device
*/
*sptr='\0';
if (!(dev=dev_get(dev_name)))
return NULL;
*sptr++=':';
 
/*
* fetch slot number
*/
slot = simple_strtoul(sptr,&eptr,10);
 
/*
* if last char is '-', it is a deletion request
*/
if (eptr[0] == '-' && !eptr[1] ) delete++;
else if (eptr[0])
return NULL;
/*
* well... let's work.
*/
if (delete)
return net_alias_dev_delete(dev, slot, err);
else
return net_alias_dev_create(dev, slot, err, sa, data);
}
 
 
/*
* rehash alias device with address supplied.
*/
 
int
net_alias_dev_rehash(struct device *dev, struct sockaddr *sa)
{
struct net_alias_info *alias_info;
struct net_alias *alias, **aliasp;
struct device *main_dev;
unsigned long flags;
struct net_alias_type *o_nat, *n_nat;
unsigned n_hash;
 
/*
* defensive ...
*/
if (dev == NULL) return -1;
if ( (alias = dev->my_alias) == NULL ) return -1;
if (!sa)
{
printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n");
return -1;
}
 
/*
* defensive. should not happen.
*/
 
if ( (main_dev = alias->main_dev) == NULL )
{
printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name);
return -1;
}
 
/*
* defensive. should not happen.
*/
 
if (!(alias_info=main_dev->alias_info))
{
printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name);
return -1;
}
/*
* will the request also change device family?
*/
o_nat = alias->nat;
if (!o_nat)
{
printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name);
return -1;
}
 
/*
* point to new alias_type obj.
*/
if (o_nat->type == sa->sa_family)
n_nat = o_nat;
else
{
n_nat = nat_getbytype(sa->sa_family);
if (!n_nat)
{
printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family);
return -1;
}
}
/*
* new hash key. if same as old AND same type (family) return;
*/
n_hash = nat_hash_key(n_nat, alias_info->hash_tab_size, sa);
if (n_hash == alias->hash && o_nat == n_nat )
return 0;
 
/*
* find alias in hashed list
*/
for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
if (*aliasp == alias) break;
/*
* not found (???). try a full search
*/
if(!*aliasp)
if ((aliasp = net_alias_slow_findp(alias_info, alias)))
printk(KERN_WARNING "net_alias_rehash(%s): bad hashing recovered\n", alias->name);
else
{
printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name);
return -1;
}
save_flags(flags);
cli();
/*
* if type (family) changed, unlink from old type object (o_nat)
* will call o_nat->alias_done_1()
*/
if (o_nat != n_nat)
nat_unbind(o_nat, alias);
 
/*
* if diff hash key, change alias position in hashed list
*/
if (n_hash != alias->hash)
{
*aliasp = (*aliasp)->next;
alias->hash = n_hash;
aliasp = &alias_info->hash_tab[n_hash];
alias->next = *aliasp;
*aliasp = alias;
}
/*
* if type (family) changed link to new type object (n_nat)
* will call n_nat->alias_init_1()
*/
if (o_nat != n_nat)
nat_bind(n_nat, alias, sa);
restore_flags(flags);
return 0;
}
 
 
 
 
/*
* implements /proc/net/alias_types entry
* shows net_alias_type objects registered.
*/
 
int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
{
off_t pos=0, begin=0;
int len=0;
struct net_alias_type *nat;
unsigned idx;
len=sprintf(buffer,"type name n_attach\n");
for (idx=0 ; idx < 16 ; idx++)
for (nat = net_alias_type_base[idx]; nat ; nat = nat->next)
{
len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
nat->type, nat->name,nat->n_attach);
pos=begin+len;
if(pos<offset)
{
len=0;
begin=pos;
}
if(pos>offset+length)
break;
}
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
len=length;
return len;
}
 
 
/*
* implements /proc/net/aliases entry, shows alias devices.
* calls alias nat->alias_print_1 if not NULL and formats everything
* to a fixed rec. size without using local (stack) buffers
*
*/
 
#define NET_ALIASES_RECSIZ 64
int net_alias_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
{
off_t pos=0, begin=0;
int len=0;
int dlen;
struct net_alias_type *nat;
struct net_alias *alias;
struct device *dev;
 
len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device family address");
for (dev = dev_base; dev ; dev = dev->next)
if (net_alias_is(dev))
{
alias = dev->my_alias;
nat = alias->nat;
dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family);
/*
* call alias_type specific print function.
*/
if (nat->alias_print_1)
dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen);
else
dlen += sprintf(buffer+len+dlen, "-");
 
/*
* fill with spaces if needed
*/
if (dlen < NET_ALIASES_RECSIZ) memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen);
/*
* truncate to NET_ALIASES_RECSIZ
*/
len += NET_ALIASES_RECSIZ;
buffer[len-1] = '\n';
pos=begin+len;
if(pos<offset)
{
len=0;
begin=pos;
}
if(pos>offset+length)
break;
}
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
len=length;
return len;
}
 
 
/*
* notifier for devices events
*/
 
int net_alias_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct device *dev = ptr;
 
if (event == NETDEV_DOWN)
{
#ifdef ALIAS_USER_LAND_DEBUG
printk("net_alias: NETDEV_DOWN for %s received\n", dev->name);
#endif
if (net_alias_has(dev))
net_alias_free(dev);
}
 
if (event == NETDEV_UP)
{
#ifdef ALIAS_USER_LAND_DEBUG
printk("net_alias: NETDEV_UP for %s received\n", dev->name);
#endif
dev->alias_info = 0;
}
 
return NOTIFY_DONE;
}
 
 
/*
* device aliases address comparison workhorse
* no checks for nat and alias_info, must be !NULL
*/
 
static __inline__ struct device *
nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
{
struct net_alias *alias;
unsigned hsize = alias_info->hash_tab_size;
for(alias = alias_info->hash_tab[nat_hash_key(nat,hsize,sa)];
alias; alias = alias->next)
{
if (alias->dev.family != sa->sa_family) continue;
 
/*
* nat_dev_addr_chk_1 will call type specific address cmp function.
*/
if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
nat_dev_addr_chk_1(nat,&alias->dev,sa))
return &alias->dev;
}
return NULL;
}
 
/*
* nat_addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
* note that nat pointer is ignored because of static comparison.
*/
 
static __inline__ struct device *
nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off)
{
struct net_alias *alias;
unsigned hsize = alias_info->hash_tab_size;
for (alias=alias_info->hash_tab[hash_key(hsize, addr32)];
alias; alias=alias->next)
{
if (alias->dev.family != family) continue;
/*
* "hard" (static) comparison between addr32 and pa_addr.
*/
if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
addr32 == alias->dev.pa_addr)
return &alias->dev;
}
return NULL;
}
 
/*
* returns alias device with specified address AND flags_on AND flags_off,
* else NULL.
* intended for main devices.
*/
 
struct device *
net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int flags_off)
{
struct net_alias_info *alias_info = main_dev->alias_info;
struct net_alias_type *nat;
/*
* only if main_dev has aliases
*/
 
if (!alias_info) return NULL;
/*
* get alias_type object for sa->sa_family.
*/
nat = nat_getbytype(sa->sa_family);
if (!nat)
return NULL;
 
return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off);
}
 
/*
* net_alias_dev_chk enough for protocols whose addr is (fully) stored
* at pa_addr.
*/
 
struct device *
net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32,
int flags_on, int flags_off)
{
struct net_alias_info *alias_info = main_dev->alias_info;
/*
* only if main_dev has aliases
*/
 
if (!alias_info) return NULL;
return nat_addr_chk32(NULL, alias_info, family, addr32, flags_on, flags_off);
}
 
 
/*
* select closest (main or alias) device to <src,dst> addresses given. if no
* further info is available, return main_dev (for easier calling arrangement).
*
* Should be called early at xxx_rcv() time for device selection
*/
 
struct device *
net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
{
int family;
struct net_alias_type *nat;
struct net_alias_info *alias_info;
struct device *dev;
if (main_dev == NULL) return NULL;
 
/*
* if not aliased, don't bother any more
*/
 
if ((alias_info = main_dev->alias_info) == NULL)
return main_dev;
 
/*
* find out family
*/
 
family = (sa_src)? sa_src->sa_family : ((sa_dst)? sa_dst->sa_family : AF_UNSPEC);
if (family == AF_UNSPEC) return main_dev;
 
/*
* get net_alias_type object for this family
*/
 
if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
/*
* first step: find out if dst addr is main_dev's or one of its aliases'
*/
 
if (sa_dst)
{
if (nat_dev_addr_chk_1(nat, main_dev,sa_dst))
return main_dev;
 
dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
 
if (dev != NULL) {
net_alias_inc_rx(dev->my_alias);
return dev;
}
}
 
/*
* second step: find the rcv addr 'closest' alias through nat method call
*/
 
if ( sa_src == NULL || nat->dev_select == NULL) return main_dev;
dev = nat->dev_select(nat, main_dev, sa_src);
 
if (dev == NULL || dev->family != family) return main_dev;
/*
* dev ok only if it is alias of main_dev
*/
 
if (net_alias_is(dev)) {
struct net_alias *alias=dev->my_alias;
if (alias->main_dev == main_dev) {
net_alias_inc_rx(alias);
return dev;
}
}
/*
* do not return NULL.
*/
return main_dev;
 
}
 
/*
* dev_rx32: dev_rx selection for 'pa_addr' protocols.
*/
 
struct device *
net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst)
{
struct net_alias_type *nat;
struct net_alias_info *alias_info;
struct sockaddr_in sin_src;
struct device *dev;
if (main_dev == NULL) return NULL;
 
/*
* if not aliased, don't bother any more
*/
 
if ((alias_info = main_dev->alias_info) == NULL)
return main_dev;
 
/*
* early return if dst is main_dev's address
*/
if (dst == main_dev->pa_addr)
return main_dev;
if (family == AF_UNSPEC) return main_dev;
 
/*
* get net_alias_type object for this family
*/
 
if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
/*
* first step: find out if dst address one of main_dev aliases'
*/
if (dst)
{
dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
if (dev) {
net_alias_inc_rx(dev->my_alias);
return dev;
}
}
/*
* second step: find the rcv addr 'closest' alias through nat method call
*/
 
if ( src == 0 || nat->dev_select == NULL) return main_dev;
 
sin_src.sin_family = family;
sin_src.sin_addr.s_addr = src;
dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src);
 
if (dev == NULL || dev->family != family) return main_dev;
/*
* dev ok only if it is alias of main_dev
*/
 
if (net_alias_is(dev)) {
struct net_alias *alias=dev->my_alias;
if (alias->main_dev == main_dev) {
net_alias_inc_rx(alias);
return dev;
}
}
 
/*
* do not return NULL.
*/
return main_dev;
}
 
/*
* device event hook
*/
 
static struct notifier_block net_alias_dev_notifier = {
net_alias_device_event,
NULL,
0
};
 
 
/*
* net_alias initialisation
* called from net_dev_init().
*/
 
#ifndef ALIAS_USER_LAND_DEBUG
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry pde1 = {
PROC_NET_ALIAS_TYPES, 11, "alias_types",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
net_alias_types_getinfo
};
static struct proc_dir_entry pde2 = {
PROC_NET_ALIASES, 7, "aliases",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
net_alias_getinfo
};
#endif
#endif
 
void net_alias_init(void)
{
 
/*
* register dev events notifier
*/
register_netdevice_notifier(&net_alias_dev_notifier);
 
/*
* register /proc/net entries
*/
#ifndef ALIAS_USER_LAND_DEBUG
#ifdef CONFIG_PROC_FS
proc_net_register(&pde1);
proc_net_register(&pde2);
#endif
#endif
}
 
/*
* net_alias type object registering func.
*/
int register_net_alias_type(struct net_alias_type *nat, int type)
{
unsigned hash;
unsigned long flags;
if (!nat)
{
printk(KERN_ERR "register_net_alias_type(): NULL arg\n");
return -EINVAL;
}
nat->type = type;
nat->n_attach = 0;
hash = nat->type & 0x0f;
save_flags(flags);
cli();
nat->next = net_alias_type_base[hash];
net_alias_type_base[hash] = nat;
restore_flags(flags);
return 0;
}
 
/*
* net_alias type object unreg.
*/
int unregister_net_alias_type(struct net_alias_type *nat)
{
struct net_alias_type **natp;
unsigned hash;
unsigned long flags;
if (!nat)
{
printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n");
return -EINVAL;
}
 
/*
* only allow unregistration if it has no attachments
*/
if (nat->n_attach)
{
printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n",
nat->n_attach);
return -EINVAL;
}
hash = nat->type & 0x0f;
save_flags(flags);
cli();
for (natp = &net_alias_type_base[hash]; *natp ; natp = &(*natp)->next)
{
if (nat==(*natp))
{
*natp = nat->next;
restore_flags(flags);
return 0;
}
}
restore_flags(flags);
printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type);
return -EINVAL;
}
 
/*
* Log sysctl's net_alias_max changes.
*/
int proc_do_net_alias_max(ctl_table *ctl, int write, struct file *filp,
void *buffer, size_t *lenp)
{
int old = sysctl_net_alias_max;
int ret;
ret = proc_dointvec(ctl, write, filp, buffer, lenp);
if (write) {
if (sysctl_net_alias_max != old) {
printk(KERN_INFO "sysctl: net_alias_max changed (max.aliases=%d, hashsize=%d).\n",
sysctl_net_alias_max,
NET_ALIAS_HASH_TAB_SIZE(sysctl_net_alias_max));
if (!sysctl_net_alias_max)
printk(KERN_INFO "sysctl: net_alias creation disabled.\n");
if (!old)
printk(KERN_INFO "sysctl: net_alias creation enabled.\n");
}
}
return ret;
}
/trunk/uclinux/uClinux-2.0.x/net/core/core.o
0,0 → 1,94
+E Gepc H  p@mL5kLBlUCJg`dL<bL8lMCJ^`lNCJY`VL@a$l(a$D*J`a l,a D>`lICJ9`lSCJ4`1L4`@+P;`+P5`lOcc\`adddddd` lWCJ```+` `0+``P!A AHH!(!HP`lK`* jj`!AHH! !H!HH!!HP`p C*` %`g j< g.` npaJ a` + n`g!A HH!!HP`p C*` %`gj8 g.` npaJa` + n`g!A HH!#0 `8ede _c cc d0`cHHc+ kc#   kNetworking buffers in use : %u +<6>Network buffers locked by drivers : %u +<6>Total network buffer allocations : %u +<6>Total failed network buffer allocs : %u +<6>Total free while locked events : %u +!H&`c cR`c c~`c c`c  c`c c!HH! <2>kfree_skb: skb = NULL (from %p) +<4>Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p) +<4>Warning: kfree_skb passed an skb still on a list (from %p). +!HP `p@.P +a, pne#P a$ne#P `ba$ Za$nbcc a, n#` a,- $` H nN*` 6j#` 0` j.*0` pa Jjc` +a jHcc#`H jpaJ jc` +an|n!A HH!(<3>alloc_skb called nonatomically from interrupt %p +!HP `pC`c$( &` `c(  a jC Jj`& /fP cl b t e g h f iPp x       P T X \  jc0| 0 0p0  dc`!A HH!!HP`p |a(Ntjc# +a(, /x@R, a$Ltjc# +l|a rca tarca!A AHH!,!HP`p p@,Pl8`` tnx#P aNtjc +aa@Ja + a@Ja + ` b` d el px      j!A HH! !H P`plpclK`* \`|lc8|j0 +( + 08@c@ + D( + +0`c`c @|j| ` + + +l +l + l$cp +$(p + (l,cp +,l8 +8l< +
/trunk/uclinux/uClinux-2.0.x/net/core/iovec.c
0,0 → 1,106
/*
* iovec manipulation routines.
*
*
* 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.
*
* Fixes:
* Andrew Lunn : Errors in iovec copying.
*/
 
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/net.h>
#include <asm/segment.h>
 
 
extern inline int min(int x, int y)
{
return x>y?y:x;
}
 
int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
{
int err=0;
int len=0;
int ct;
if(m->msg_name!=NULL)
{
if(mode==VERIFY_READ) {
err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
} else
err=verify_area(mode, m->msg_name, m->msg_namelen);
if(err<0)
return err;
m->msg_name = address;
}
if(m->msg_control!=NULL)
{
err=verify_area(mode, m->msg_control, m->msg_controllen);
if(err)
return err;
}
for(ct=0;ct<m->msg_iovlen;ct++)
{
err=verify_area(VERIFY_READ, &m->msg_iov[ct], sizeof(struct iovec));
if(err)
return err;
memcpy_fromfs(&iov[ct], &m->msg_iov[ct], sizeof(struct iovec));
err=verify_area(mode, iov[ct].iov_base, iov[ct].iov_len);
if(err)
return err;
len+=iov[ct].iov_len;
}
m->msg_iov=&iov[0];
return len;
}
 
/*
* Copy kernel to iovec.
*/
void memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
{
while(len>0)
{
if(iov->iov_len)
{
int copy = min(iov->iov_len,len);
memcpy_tofs(iov->iov_base,kdata,copy);
kdata+=copy;
len-=copy;
iov->iov_len-=copy;
iov->iov_base+=copy;
}
iov++;
}
}
 
/*
* Copy iovec to kernel.
*/
void memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
{
while(len>0)
{
if(iov->iov_len)
{
int copy=min(len,iov->iov_len);
memcpy_fromfs(kdata, iov->iov_base, copy);
len-=copy;
kdata+=copy;
iov->iov_base+=copy;
iov->iov_len-=copy;
}
iov++;
}
}
/trunk/uclinux/uClinux-2.0.x/net/core/firewall.c
0,0 → 1,166
/*
* Generic loadable firewalls. At the moment only IP will actually
* use these, but people can add the others as they are needed.
*
* Authors: Dave Bonn (for IP)
* much hacked by: Alan Cox
*/
 
#include <linux/module.h>
#include <linux/config.h>
#include <linux/skbuff.h>
#include <linux/firewall.h>
 
static int firewall_lock=0;
static int firewall_policy[NPROTO];
static struct firewall_ops *firewall_chain[NPROTO];
 
/*
* Register a firewall
*/
int register_firewall(int pf, struct firewall_ops *fw)
{
struct firewall_ops **p;
if(pf<0||pf>=NPROTO)
return -EINVAL;
/*
* Don't allow two people to adjust at once.
*/
while(firewall_lock)
schedule();
firewall_lock=1;
p=&firewall_chain[pf];
while(*p)
{
if(fw->fw_priority > (*p)->fw_priority)
break;
p=&((*p)->next);
}
 
/*
* We need to use a memory barrier to make sure that this
* works correctly even in SMP with weakly ordered writes.
*
* This is atomic wrt interrupts (and generally walking the
* chain), but not wrt itself (so you can't call this from
* an interrupt. Not that you'd want to).
*/
fw->next=*p;
mb();
*p = fw;
 
/*
* And release the sleep lock
*/
 
firewall_lock=0;
return 0;
}
 
/*
* Unregister a firewall
*/
 
int unregister_firewall(int pf, struct firewall_ops *fw)
{
struct firewall_ops **nl;
if(pf<0||pf>=NPROTO)
return -EINVAL;
/*
* Don't allow two people to adjust at once.
*/
while(firewall_lock)
schedule();
firewall_lock=1;
 
nl=&firewall_chain[pf];
while(*nl!=NULL)
{
if(*nl==fw)
{
struct firewall_ops *f=fw->next;
*nl = f;
firewall_lock=0;
return 0;
}
nl=&((*nl)->next);
}
firewall_lock=0;
return -ENOENT;
}
 
int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg)
{
struct firewall_ops *fw=firewall_chain[pf];
while(fw!=NULL)
{
int rc=fw->fw_forward(fw,pf,dev,phdr,arg);
if(rc!=FW_SKIP)
return rc;
fw=fw->next;
}
return firewall_policy[pf];
}
 
/*
* Actual invocation of the chains
*/
int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg)
{
struct firewall_ops *fw=firewall_chain[pf];
while(fw!=NULL)
{
int rc=fw->fw_input(fw,pf,dev,phdr,arg);
if(rc!=FW_SKIP)
return rc;
fw=fw->next;
}
return firewall_policy[pf];
}
 
int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg)
{
struct firewall_ops *fw=firewall_chain[pf];
while(fw!=NULL)
{
int rc=fw->fw_output(fw,pf,dev,phdr,arg);
if(rc!=FW_SKIP)
return rc;
fw=fw->next;
}
/* alan, is this right? */
return firewall_policy[pf];
}
 
static struct symbol_table firewall_syms = {
#include <linux/symtab_begin.h>
X(register_firewall),
X(unregister_firewall),
X(call_in_firewall),
X(call_out_firewall),
X(call_fw_firewall),
#include <linux/symtab_end.h>
};
 
void fwchain_init(void)
{
int i;
for(i=0;i<NPROTO;i++)
firewall_policy[i]=FW_ACCEPT;
register_symtab(&firewall_syms);
}
/trunk/uclinux/uClinux-2.0.x/net/core/dev_mcast.c
0,0 → 1,145
/*
* Linux NET3: Multicast List maintenance.
*
* Authors:
* Tim Kordas <tjk@nostromo.eeap.cwru.edu>
* Richard Underwood <richard@wuzz.demon.co.uk>
*
* Stir fried together from the IP multicast and CAP patches above
* Alan Cox <Alan.Cox@linux.org>
*
* Fixes:
* Alan Cox : Update the device on a real delete
* rather than any time but...
* Alan Cox : IFF_ALLMULTI support.
* Alan Cox : New format set_multicast_list() calls.
*
* 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 <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <net/ip.h>
#include <net/route.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
 
 
/*
* Device multicast list maintenance. This knows about such little matters as promiscuous mode and
* converting from the list to the array the drivers use. At least until I fix the drivers up.
*
* This is used both by IP and by the user level maintenance functions. Unlike BSD we maintain a usage count
* on a given multicast address so that a casual user application can add/delete multicasts used by protocols
* without doing damage to the protocols when it deletes the entries. It also helps IP as it tracks overlapping
* maps.
*/
 
/*
* Update the multicast list into the physical NIC controller.
*/
void dev_mc_upload(struct device *dev)
{
/* Don't do anything till we up the interface
[dev_open will call this function so the list will
stay sane] */
if(!(dev->flags&IFF_UP))
return;
/*
* Devices with no set multicast don't get set
*/
if(dev->set_multicast_list==NULL)
return;
dev->set_multicast_list(dev);
}
/*
* Delete a device level multicast
*/
void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
{
struct dev_mc_list **dmi;
for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next)
{
if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen)
{
struct dev_mc_list *tmp= *dmi;
if(--(*dmi)->dmi_users && !all)
return;
*dmi=(*dmi)->next;
dev->mc_count--;
kfree_s(tmp,sizeof(*tmp));
dev_mc_upload(dev);
return;
}
}
}
 
/*
* Add a device level multicast
*/
void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
{
struct dev_mc_list *dmi;
for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
{
if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen)
{
if(!newonly)
dmi->dmi_users++;
return;
}
}
dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi),GFP_KERNEL);
if(dmi==NULL)
return; /* GFP_KERNEL so can't happen anyway */
memcpy(dmi->dmi_addr, addr, alen);
dmi->dmi_addrlen=alen;
dmi->next=dev->mc_list;
dmi->dmi_users=1;
dev->mc_list=dmi;
dev->mc_count++;
dev_mc_upload(dev);
}
 
/*
* Discard multicast list when a device is downed
*/
 
void dev_mc_discard(struct device *dev)
{
while(dev->mc_list!=NULL)
{
struct dev_mc_list *tmp=dev->mc_list;
dev->mc_list=dev->mc_list->next;
kfree_s(tmp,sizeof(*tmp));
}
dev->mc_count=0;
}
/trunk/uclinux/uClinux-2.0.x/net/core/sock.o
0,0 → 1,14
+E Gepc H  p@mL5kLBlUCJg`dL<bL8lMCJ^`lNCJY`VL@a$l(a$D*J`a l,a D>`lICJ9`lSCJ4`1L4`@+P;`+P5`lOcc\`adddddd` lWCJ```+` `0+``P!A AHH!(!HP`lK`* jj`!AHH! !H!HH!!HP`p C*` %`g j< g.` npaJ a` + n`g!A HH!!HP`p C*` %`gj8 g.` npaJa` + n`g!A HH!#0 `8ede _c cc d0`cHHc+ kc#   k
/trunk/uclinux/uClinux-2.0.x/net/core/dev.c
0,0 → 1,1640
/*
* NET3 Protocol independent device support routines.
*
* 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.
*
* Derived from the non IP parts of dev.c 1.0.19
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Mark Evans, <evansmp@uhura.aston.ac.uk>
*
* Additional Authors:
* Florian la Roche <rzsfl@rz.uni-sb.de>
* Alan Cox <gw4pts@gw4pts.ampr.org>
* David Hinds <dhinds@allegro.stanford.edu>
*
* Changes:
* Alan Cox : device private ioctl copies fields back.
* Alan Cox : Transmit queue code does relevant stunts to
* keep the queue safe.
* Alan Cox : Fixed double lock.
* Alan Cox : Fixed promisc NULL pointer trap
* ???????? : Support the full private ioctl range
* Alan Cox : Moved ioctl permission check into drivers
* Tim Kordas : SIOCADDMULTI/SIOCDELMULTI
* Alan Cox : 100 backlog just doesn't cut it when
* you start doing multicast video 8)
* Alan Cox : Rewrote net_bh and list manager.
* Alan Cox : Fix ETH_P_ALL echoback lengths.
* Alan Cox : Took out transmit every packet pass
* Saved a few bytes in the ioctl handler
* Alan Cox : Network driver sets packet type before calling netif_rx. Saves
* a function call a packet.
* Alan Cox : Hashed net_bh()
* Richard Kooijman: Timestamp fixes.
* Alan Cox : Wrong field in SIOCGIFDSTADDR
* Alan Cox : Device lock protection.
* Alan Cox : Fixed nasty side effect of device close changes.
* Rudi Cilibrasi : Pass the right thing to set_mac_address()
* Dave Miller : 32bit quantity for the device lock to make it work out
* on a Sparc.
* Bjorn Ekwall : Added KERNELD hack.
* Alan Cox : Cleaned up the backlog initialise.
* Craig Metz : SIOCGIFCONF fix if space for under
* 1 device.
* Thomas Bogendoerfer : Return ENODEV for dev_open, if there
* is no device open function.
* Lawrence V. Stefani : Changed set MTU ioctl to not assume
* min MTU of 68 bytes for devices
* that have change MTU functions.
*
*/
 
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/notifier.h>
#include <net/ip.h>
#include <net/route.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
#include <net/slhc.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <net/br.h>
#ifdef CONFIG_NET_ALIAS
#include <linux/net_alias.h>
#endif
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif
#ifdef CONFIG_NET_RADIO
#include <linux/wireless.h>
#endif /* CONFIG_NET_RADIO */
 
/*
* The list of packet types we will receive (as opposed to discard)
* and the routines to invoke.
*/
 
struct packet_type *ptype_base[16];
struct packet_type *ptype_all = NULL; /* Taps */
 
/*
* Device list lock
*/
int dev_lockct=0;
/*
* Our notifier list
*/
struct notifier_block *netdev_chain=NULL;
 
/*
* Device drivers call our routines to queue packets here. We empty the
* queue in the bottom half handler.
*/
 
static struct sk_buff_head backlog;
 
/*
* We don't overdo the queue or we will thrash memory badly.
*/
static int backlog_size = 0;
 
/*
* Return the lesser of the two values.
*/
static __inline__ unsigned long min(unsigned long a, unsigned long b)
{
return (a < b)? a : b;
}
 
 
/******************************************************************************************
 
Protocol management and registration routines
 
*******************************************************************************************/
 
/*
* For efficiency
*/
 
static int dev_nit=0;
 
/*
* Add a protocol ID to the list. Now that the input handler is
* smarter we can dispense with all the messy stuff that used to be
* here.
*/
void dev_add_pack(struct packet_type *pt)
{
int hash;
if(pt->type==htons(ETH_P_ALL))
{
dev_nit++;
pt->next=ptype_all;
ptype_all=pt;
}
else
{
hash=ntohs(pt->type)&15;
pt->next = ptype_base[hash];
ptype_base[hash] = pt;
}
}
 
 
/*
* Remove a protocol ID from the list.
*/
void dev_remove_pack(struct packet_type *pt)
{
struct packet_type **pt1;
if(pt->type==htons(ETH_P_ALL))
{
dev_nit--;
pt1=&ptype_all;
}
else
pt1=&ptype_base[ntohs(pt->type)&15];
for(; (*pt1)!=NULL; pt1=&((*pt1)->next))
{
if(pt==(*pt1))
{
*pt1=pt->next;
return;
}
}
printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
}
 
/*****************************************************************************************
 
Device Interface Subroutines
 
******************************************************************************************/
 
/*
* Find an interface by name.
*/
struct device *dev_get(const char *name)
{
struct device *dev;
 
for (dev = dev_base; dev != NULL; dev = dev->next)
{
if (strcmp(dev->name, name) == 0)
return(dev);
}
return NULL;
}
/*
* Find and possibly load an interface.
*/
#ifdef CONFIG_KERNELD
 
extern __inline__ void dev_load(const char *name)
{
if(!dev_get(name) && suser()) {
#ifdef CONFIG_NET_ALIAS
const char *sptr;
for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break;
if (!(*sptr && *(sptr+1)))
#endif
request_module(name);
}
}
 
#endif
/*
* Prepare an interface for use.
*/
int dev_open(struct device *dev)
{
int ret = -ENODEV;
 
/*
* Call device private open method
*/
if (dev->open)
ret = dev->open(dev);
 
/*
* If it went open OK then set the flags
*/
if (ret == 0)
{
dev->flags |= (IFF_UP | IFF_RUNNING);
/*
* Initialise multicasting status
*/
dev_mc_upload(dev);
notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
}
return(ret);
}
 
 
/*
* Completely shutdown an interface.
*/
int dev_close(struct device *dev)
{
int ct=0;
 
/*
* Call the device specific close. This cannot fail.
* Only if device is UP
*/
if ((dev->flags & IFF_UP) && dev->stop)
dev->stop(dev);
 
/*
* Device is now down.
*/
dev->flags&=~(IFF_UP|IFF_RUNNING);
 
/*
* Tell people we are going down
*/
notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
/*
* Flush the multicast chain
*/
dev_mc_discard(dev);
 
/*
* Purge any queued packets when we down the link
*/
while(ct<DEV_NUMBUFFS)
{
struct sk_buff *skb;
while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
if(skb->free)
kfree_skb(skb,FREE_WRITE);
ct++;
}
return(0);
}
 
 
/*
* Device change register/unregister. These are not inline or static
* as we export them to the world.
*/
 
int register_netdevice_notifier(struct notifier_block *nb)
{
return notifier_chain_register(&netdev_chain, nb);
}
 
int unregister_netdevice_notifier(struct notifier_block *nb)
{
return notifier_chain_unregister(&netdev_chain,nb);
}
 
/*
* Send (or queue for sending) a packet.
*
* IMPORTANT: When this is called to resend frames. The caller MUST
* already have locked the sk_buff. Apart from that we do the
* rest of the magic.
*/
 
static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
unsigned long flags;
struct sk_buff_head *list;
int retransmission = 0; /* used to say if the packet should go */
/* at the front or the back of the */
/* queue - front is a retransmit try */
 
if(pri>=0 && !skb_device_locked(skb))
skb_device_lock(skb); /* Shove a lock on the frame */
#if CONFIG_SKB_CHECK
IS_SKB(skb);
#endif
skb->dev = dev;
 
/*
* Negative priority is used to flag a frame that is being pulled from the
* queue front as a retransmit attempt. It therefore goes back on the queue
* start on a failure.
*/
if (pri < 0)
{
pri = -pri-1;
retransmission = 1;
}
 
#ifdef CONFIG_NET_DEBUG
if (pri >= DEV_NUMBUFFS)
{
printk(KERN_WARNING "bad priority in dev_queue_xmit.\n");
pri = 1;
}
#endif
 
/*
* If the address has not been resolved. Call the device header rebuilder.
* This can cover all protocols and technically not just ARP either.
*/
if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
return;
}
 
/*
*
* If dev is an alias, switch to its main device.
* "arp" resolution has been made with alias device, so
* arp entries refer to alias, not main.
*
*/
 
#ifdef CONFIG_NET_ALIAS
if (net_alias_is(dev))
skb->dev = dev = net_alias_dev_tx(dev);
#endif
 
/*
* If we are bridging and this is directly generated output
* pass the frame via the bridge.
*/
 
#ifdef CONFIG_BRIDGE
if(skb->pkt_bridged!=IS_BRIDGED && br_stats.flags & BR_UP)
{
if(br_tx_frame(skb))
return;
}
#endif
 
list = dev->buffs + pri;
 
save_flags(flags);
/* if this isn't a retransmission, use the first packet instead... */
if (!retransmission) {
if (skb_queue_len(list)) {
/* avoid overrunning the device queue.. */
if (skb_queue_len(list) > dev->tx_queue_len) {
dev_kfree_skb(skb, FREE_WRITE);
return;
}
}
 
/* copy outgoing packets to any sniffer packet handlers */
if (dev_nit) {
struct packet_type *ptype;
skb->stamp=xtime;
for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next)
{
/* Never send packets back to the socket
* they originated from - MvS (miquels@drinkel.ow.org)
*/
if ((ptype->dev == dev || !ptype->dev) &&
((struct sock *)ptype->data != skb->sk))
{
struct sk_buff *skb2;
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
break;
/* FIXME?: Wrong when the hard_header_len
* is an upper bound. Is this even
* used anywhere?
*/
skb2->h.raw = skb2->data + dev->hard_header_len;
/* On soft header devices we
* yank the header before mac.raw
* back off. This is set by
* dev->hard_header().
*/
if (dev->flags&IFF_SOFTHEADERS)
skb_pull(skb2,skb2->mac.raw-skb2->data);
skb2->mac.raw = skb2->data;
ptype->func(skb2, skb->dev, ptype);
}
}
}
 
if (skb_queue_len(list)) {
cli();
skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */
__skb_queue_tail(list, skb);
skb = __skb_dequeue(list);
skb_device_lock(skb); /* New buffer needs locking down */
restore_flags(flags);
}
}
if (dev->hard_start_xmit(skb, dev) == 0) {
/*
* Packet is now solely the responsibility of the driver
*/
return;
}
 
/*
* Transmission failed, put skb back into a list. Once on the list it's safe and
* no longer device locked (it can be freed safely from the device queue)
*/
cli();
skb_device_unlock(skb);
__skb_queue_head(list,skb);
restore_flags(flags);
}
 
void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
start_bh_atomic();
do_dev_queue_xmit(skb, dev, pri);
end_bh_atomic();
}
 
/*
* Receive a packet from a device driver and queue it for the upper
* (protocol) levels. It always succeeds. This is the recommended
* interface to use.
*/
 
void netif_rx(struct sk_buff *skb)
{
static int dropping = 0;
 
/*
* Any received buffers are un-owned and should be discarded
* when freed. These will be updated later as the frames get
* owners.
*/
 
skb->sk = NULL;
skb->free = 1;
if(skb->stamp.tv_sec==0)
skb->stamp = xtime;
 
/*
* Check that we aren't overdoing things.
*/
 
if (!backlog_size)
dropping = 0;
else if (backlog_size > 300)
dropping = 1;
 
if (dropping)
{
kfree_skb(skb, FREE_READ);
return;
}
 
/*
* Add it to the "backlog" queue.
*/
#if CONFIG_SKB_CHECK
IS_SKB(skb);
#endif
skb_queue_tail(&backlog,skb);
backlog_size++;
/*
* If any packet arrived, mark it for processing after the
* hardware interrupt returns.
*/
 
mark_bh(NET_BH);
return;
}
 
/*
* This routine causes all interfaces to try to send some data.
*/
static void dev_transmit(void)
{
struct device *dev;
 
for (dev = dev_base; dev != NULL; dev = dev->next)
{
if (dev->flags != 0 && !dev->tbusy) {
/*
* Kick the device
*/
dev_tint(dev);
}
}
}
 
 
/**********************************************************************************
 
Receive Queue Processor
***********************************************************************************/
 
/*
* When we are called the queue is ready to grab, the interrupts are
* on and hardware can interrupt and queue to the receive queue as we
* run with no problems.
* This is run as a bottom half after an interrupt handler that does
* mark_bh(NET_BH);
*/
void net_bh(void)
{
struct packet_type *ptype;
struct packet_type *pt_prev;
unsigned short type;
 
/*
* Can we send anything now? We want to clear the
* decks for any more sends that get done as we
* process the input. This also minimises the
* latency on a transmit interrupt bh.
*/
 
dev_transmit();
/*
* Any data left to process. This may occur because a
* mark_bh() is done after we empty the queue including
* that from the device which does a mark_bh() just after
*/
 
/*
* While the queue is not empty..
*
* Note that the queue never shrinks due to
* an interrupt, so we can do this test without
* disabling interrupts.
*/
 
while (!skb_queue_empty(&backlog)) {
struct sk_buff * skb = backlog.next;
 
/*
* We have a packet. Therefore the queue has shrunk
*/
cli();
__skb_unlink(skb, &backlog);
backlog_size--;
sti();
 
#ifdef CONFIG_BRIDGE
 
/*
* If we are bridging then pass the frame up to the
* bridging code. If it is bridged then move on
*/
if (br_stats.flags & BR_UP)
{
/*
* We pass the bridge a complete frame. This means
* recovering the MAC header first.
*/
int offset=skb->data-skb->mac.raw;
cli();
skb_push(skb,offset); /* Put header back on for bridge */
if(br_receive_frame(skb))
{
sti();
continue;
}
/*
* Pull the MAC header off for the copy going to
* the upper layers.
*/
skb_pull(skb,offset);
sti();
}
#endif
/*
* Bump the pointer to the next structure.
*
* On entry to the protocol layer. skb->data and
* skb->h.raw point to the MAC and encapsulated data
*/
 
skb->h.raw = skb->data;
 
/*
* Fetch the packet protocol ID.
*/
type = skb->protocol;
 
/*
* We got a packet ID. Now loop over the "known protocols"
* list. There are two lists. The ptype_all list of taps (normally empty)
* and the main protocol list which is hashed perfectly for normal protocols.
*/
pt_prev = NULL;
for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
{
if(!ptype->dev || ptype->dev == skb->dev) {
if(pt_prev) {
struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
if(skb2)
pt_prev->func(skb2,skb->dev, pt_prev);
}
pt_prev=ptype;
}
}
for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next)
{
if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev))
{
/*
* We already have a match queued. Deliver
* to it and then remember the new match
*/
if(pt_prev)
{
struct sk_buff *skb2;
 
skb2=skb_clone(skb, GFP_ATOMIC);
 
/*
* Kick the protocol handler. This should be fast
* and efficient code.
*/
 
if(skb2)
pt_prev->func(skb2, skb->dev, pt_prev);
}
/* Remember the current last to do */
pt_prev=ptype;
}
} /* End of protocol list loop */
/*
* Is there a last item to send to ?
*/
 
if(pt_prev)
pt_prev->func(skb, skb->dev, pt_prev);
/*
* Has an unknown packet has been received ?
*/
else
kfree_skb(skb, FREE_WRITE);
/*
* Again, see if we can transmit anything now.
* [Ought to take this out judging by tests it slows
* us down not speeds us up]
*/
#ifdef XMIT_EVERY
dev_transmit();
#endif
} /* End of queue loop */
/*
* We have emptied the queue
*/
/*
* One last output flush.
*/
 
#ifdef XMIT_AFTER
dev_transmit();
#endif
}
 
 
/*
* This routine is called when an device driver (i.e. an
* interface) is ready to transmit a packet.
*/
void dev_tint(struct device *dev)
{
int i;
unsigned long flags;
struct sk_buff_head * head;
/*
* aliases do not transmit (for now :) )
*/
 
#ifdef CONFIG_NET_ALIAS
if (net_alias_is(dev)) return;
#endif
head = dev->buffs;
save_flags(flags);
cli();
 
/*
* Work the queues in priority order
*/
for(i = 0;i < DEV_NUMBUFFS; i++,head++)
{
 
while (!skb_queue_empty(head)) {
struct sk_buff *skb;
 
skb = head->next;
__skb_unlink(skb, head);
/*
* Stop anyone freeing the buffer while we retransmit it
*/
skb_device_lock(skb);
restore_flags(flags);
/*
* Feed them to the output stage and if it fails
* indicate they re-queue at the front.
*/
do_dev_queue_xmit(skb,dev,-i - 1);
/*
* If we can take no more then stop here.
*/
if (dev->tbusy)
return;
cli();
}
}
restore_flags(flags);
}
 
 
/*
* Perform a SIOCGIFCONF call. This structure will change
* size shortly, and there is nothing I can do about it.
* Thus we will need a 'compatibility mode'.
*/
 
static int dev_ifconf(char *arg)
{
struct ifconf ifc;
struct ifreq ifr;
struct device *dev;
char *pos;
int len;
int err;
 
/*
* Fetch the caller's info block.
*/
err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
if(err)
return err;
memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
len = ifc.ifc_len;
pos = ifc.ifc_buf;
 
/*
* We now walk the device list filling each active device
* into the array.
*/
err=verify_area(VERIFY_WRITE,pos,len);
if(err)
return err;
/*
* Loop over the interfaces, and write an info block for each.
*/
 
for (dev = dev_base; dev != NULL; dev = dev->next)
{
if(!(dev->flags & IFF_UP)) /* Downed devices don't count */
continue;
/*
* Have we run out of space here ?
*/
if (len < sizeof(struct ifreq))
break;
 
memset(&ifr, 0, sizeof(struct ifreq));
strcpy(ifr.ifr_name, dev->name);
(*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
(*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
 
 
/*
* Write this block to the caller's space.
*/
memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
pos += sizeof(struct ifreq);
len -= sizeof(struct ifreq);
}
 
/*
* All done. Write the updated control block back to the caller.
*/
ifc.ifc_len = (pos - ifc.ifc_buf);
ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
/*
* Report how much was filled in
*/
return(pos - arg);
}
 
 
/*
* This is invoked by the /proc filesystem handler to display a device
* in detail.
*/
 
#ifdef CONFIG_PROC_FS
static int sprintf_stats(char *buffer, struct device *dev)
{
struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
int size;
if (stats)
size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
dev->name,
stats->rx_packets, stats->rx_errors,
stats->rx_dropped + stats->rx_missed_errors,
stats->rx_fifo_errors,
stats->rx_length_errors + stats->rx_over_errors
+ stats->rx_crc_errors + stats->rx_frame_errors,
stats->tx_packets, stats->tx_errors, stats->tx_dropped,
stats->tx_fifo_errors, stats->collisions,
stats->tx_carrier_errors + stats->tx_aborted_errors
+ stats->tx_window_errors + stats->tx_heartbeat_errors);
else
size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
 
return size;
}
 
/*
* Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface
* to create /proc/net/dev
*/
int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
int len=0;
off_t begin=0;
off_t pos=0;
int size;
struct device *dev;
 
 
size = sprintf(buffer, "Inter-| Receive | Transmit\n"
" face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
pos+=size;
len+=size;
 
for (dev = dev_base; dev != NULL; dev = dev->next)
{
size = sprintf_stats(buffer+len, dev);
len+=size;
pos=begin+len;
if(pos<offset)
{
len=0;
begin=pos;
}
if(pos>offset+length)
break;
}
*start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Start slop */
if(len>length)
len=length; /* Ending slop */
return len;
}
#endif /* CONFIG_PROC_FS */
 
 
#ifdef CONFIG_NET_RADIO
#ifdef CONFIG_PROC_FS
 
/*
* Print one entry of /proc/net/wireless
* This is a clone of /proc/net/dev (just above)
*/
static int
sprintf_wireless_stats(char * buffer,
struct device * dev)
{
/* Get stats from the driver */
struct iw_statistics *stats = (dev->get_wireless_stats ?
dev->get_wireless_stats(dev) :
(struct iw_statistics *) NULL);
int size;
if(stats != (struct iw_statistics *) NULL)
size = sprintf(buffer,
"%6s: %02x %3d%c %3d%c %3d%c %5d %5d %5d\n",
dev->name,
stats->status,
stats->qual.qual,
stats->qual.updated & 1 ? '.' : ' ',
stats->qual.level,
stats->qual.updated & 2 ? '.' : ' ',
stats->qual.noise,
stats->qual.updated & 3 ? '.' : ' ',
stats->discard.nwid,
stats->discard.code,
stats->discard.misc);
else
size = 0;
 
return size;
}
 
/*
* Print info for /proc/net/wireless (print all entries)
* This is a clone of /proc/net/dev (just above)
*/
int
dev_get_wireless_info(char * buffer,
char ** start,
off_t offset,
int length,
int dummy)
{
int len = 0;
off_t begin = 0;
off_t pos = 0;
int size;
 
struct device * dev;
 
size = sprintf(buffer,
"Inter-|sta| Quality | Discarded packets\n"
" face |tus|link level noise| nwid crypt misc\n");
pos+=size;
len+=size;
 
 
for(dev = dev_base; dev != NULL; dev = dev->next)
{
size = sprintf_wireless_stats(buffer+len, dev);
len+=size;
pos=begin+len;
 
if(pos < offset)
{
len=0;
begin=pos;
}
if(pos > offset + length)
break;
}
 
*start = buffer + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Start slop */
if(len > length)
len = length; /* Ending slop */
 
return len;
}
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_RADIO */
 
 
/*
* This checks bitmasks for the ioctl calls for devices.
*/
static inline int bad_mask(unsigned long mask, unsigned long addr)
{
if (addr & (mask = ~mask))
return 1;
mask = ntohl(mask);
if (mask & (mask+1))
return 1;
return 0;
}
 
/*
* Perform the SIOCxIFxxx calls.
*
* The socket layer has seen an ioctl the address family thinks is
* for the device. At this point we get invoked to make a decision
*/
static int dev_ifsioc(void *arg, unsigned int getset)
{
struct ifreq ifr;
struct device *dev;
int ret;
 
/*
* Fetch the caller's info block into kernel space
*/
 
int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
if(err)
return err;
memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
 
/*
* See which interface the caller is talking about.
*/
/*
*
* net_alias_dev_get(): dev_get() with added alias naming magic.
* only allow alias creation/deletion if (getset==SIOCSIFADDR)
*
*/
#ifdef CONFIG_KERNELD
dev_load(ifr.ifr_name);
#endif
 
#ifdef CONFIG_NET_ALIAS
if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
return(err);
#else
if ((dev = dev_get(ifr.ifr_name)) == NULL)
return(-ENODEV);
#endif
switch(getset)
{
case SIOCGIFFLAGS: /* Get interface flags */
ifr.ifr_flags = (dev->flags & ~IFF_SOFTHEADERS);
goto rarok;
 
case SIOCSIFFLAGS: /* Set interface flags */
{
int old_flags = dev->flags;
if(securelevel>0)
ifr.ifr_flags&=~IFF_PROMISC;
/*
* We are not allowed to potentially close/unload
* a device until we get this lock.
*/
dev_lock_wait();
/*
* Set the flags on our device.
*/
dev->flags = (ifr.ifr_flags & (
IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
| IFF_MULTICAST)) | (dev->flags & (IFF_SOFTHEADERS|IFF_UP));
/*
* Load in the correct multicast list now the flags have changed.
*/
 
dev_mc_upload(dev);
 
/*
* Have we downed the interface. We handle IFF_UP ourselves
* according to user attempts to set it, rather than blindly
* setting it.
*/
if ((old_flags^ifr.ifr_flags)&IFF_UP) /* Bit is different ? */
{
if(old_flags&IFF_UP) /* Gone down */
ret=dev_close(dev);
else /* Come up */
{
ret=dev_open(dev);
if(ret<0)
dev->flags&=~IFF_UP; /* Open failed */
}
}
else
ret=0;
/*
* Load in the correct multicast list now the flags have changed.
*/
 
dev_mc_upload(dev);
}
break;
case SIOCGIFADDR: /* Get interface address (and family) */
if(ifr.ifr_addr.sa_family==AF_UNSPEC)
{
memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
ifr.ifr_hwaddr.sa_family=dev->type;
goto rarok;
}
else
{
(*(struct sockaddr_in *)
&ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
(*(struct sockaddr_in *)
&ifr.ifr_addr).sin_family = dev->family;
(*(struct sockaddr_in *)
&ifr.ifr_addr).sin_port = 0;
}
goto rarok;
case SIOCSIFADDR: /* Set interface address (and family) */
/*
* BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
* physical address. We can cope with this now.
*/
if(ifr.ifr_addr.sa_family==AF_UNSPEC)
{
if(dev->set_mac_address==NULL)
return -EOPNOTSUPP;
if(securelevel>0)
return -EPERM;
ret=dev->set_mac_address(dev,&ifr.ifr_addr);
}
else
{
u32 new_pa_addr = (*(struct sockaddr_in *)
&ifr.ifr_addr).sin_addr.s_addr;
u16 new_family = ifr.ifr_addr.sa_family;
 
if (new_family == dev->family &&
new_pa_addr == dev->pa_addr) {
ret =0;
break;
}
if (dev->flags & IFF_UP)
notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
 
/*
* if dev is an alias, must rehash to update
* address change
*/
 
#ifdef CONFIG_NET_ALIAS
if (net_alias_is(dev))
net_alias_dev_rehash(dev ,&ifr.ifr_addr);
#endif
dev->pa_addr = new_pa_addr;
dev->family = new_family;
#ifdef CONFIG_INET
/* This is naughty. When net-032e comes out It wants moving into the net032
code not the kernel. Till then it can sit here (SIGH) */
if (!dev->pa_mask)
dev->pa_mask = ip_get_mask(dev->pa_addr);
#endif
if (!dev->pa_brdaddr)
dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
if (dev->flags & IFF_UP)
notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
ret = 0;
}
break;
case SIOCGIFBRDADDR: /* Get the broadcast address */
(*(struct sockaddr_in *)
&ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
(*(struct sockaddr_in *)
&ifr.ifr_broadaddr).sin_family = dev->family;
(*(struct sockaddr_in *)
&ifr.ifr_broadaddr).sin_port = 0;
goto rarok;
 
case SIOCSIFBRDADDR: /* Set the broadcast address */
dev->pa_brdaddr = (*(struct sockaddr_in *)
&ifr.ifr_broadaddr).sin_addr.s_addr;
ret = 0;
break;
case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */
(*(struct sockaddr_in *)
&ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
(*(struct sockaddr_in *)
&ifr.ifr_dstaddr).sin_family = dev->family;
(*(struct sockaddr_in *)
&ifr.ifr_dstaddr).sin_port = 0;
goto rarok;
case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */
dev->pa_dstaddr = (*(struct sockaddr_in *)
&ifr.ifr_dstaddr).sin_addr.s_addr;
ret = 0;
break;
case SIOCGIFNETMASK: /* Get the netmask for the interface */
(*(struct sockaddr_in *)
&ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
(*(struct sockaddr_in *)
&ifr.ifr_netmask).sin_family = dev->family;
(*(struct sockaddr_in *)
&ifr.ifr_netmask).sin_port = 0;
goto rarok;
 
case SIOCSIFNETMASK: /* Set the netmask for the interface */
{
unsigned long mask = (*(struct sockaddr_in *)
&ifr.ifr_netmask).sin_addr.s_addr;
ret = -EINVAL;
/*
* The mask we set must be legal.
*/
if (bad_mask(mask,0))
break;
dev->pa_mask = mask;
ret = 0;
}
break;
case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */
ifr.ifr_metric = dev->metric;
goto rarok;
case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */
dev->metric = ifr.ifr_metric;
ret=0;
break;
case SIOCGIFMTU: /* Get the MTU of a device */
ifr.ifr_mtu = dev->mtu;
goto rarok;
case SIOCSIFMTU: /* Set the MTU of a device */
if (dev->change_mtu)
ret = dev->change_mtu(dev, ifr.ifr_mtu);
else
{
/*
* MTU must be positive.
*/
if(ifr.ifr_mtu<68)
return -EINVAL;
 
dev->mtu = ifr.ifr_mtu;
ret = 0;
}
break;
case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently
do not support it */
ret = -EINVAL;
break;
case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */
ret = -EINVAL;
break;
 
case SIOCGIFHWADDR:
memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
ifr.ifr_hwaddr.sa_family=dev->type;
goto rarok;
case SIOCSIFHWADDR:
if(dev->set_mac_address==NULL)
return -EOPNOTSUPP;
if(securelevel > 0)
return -EPERM;
if(ifr.ifr_hwaddr.sa_family!=dev->type)
return -EINVAL;
ret=dev->set_mac_address(dev,&ifr.ifr_hwaddr);
break;
case SIOCGIFMAP:
ifr.ifr_map.mem_start=dev->mem_start;
ifr.ifr_map.mem_end=dev->mem_end;
ifr.ifr_map.base_addr=dev->base_addr;
ifr.ifr_map.irq=dev->irq;
ifr.ifr_map.dma=dev->dma;
ifr.ifr_map.port=dev->if_port;
goto rarok;
case SIOCSIFMAP:
if(dev->set_config==NULL)
return -EOPNOTSUPP;
return dev->set_config(dev,&ifr.ifr_map);
case SIOCADDMULTI:
if(dev->set_multicast_list==NULL)
return -EINVAL;
if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
return -EINVAL;
dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1);
return 0;
 
case SIOCDELMULTI:
if(dev->set_multicast_list==NULL)
return -EINVAL;
if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
return -EINVAL;
dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1);
return 0;
/*
* Unknown or private ioctl
*/
 
default:
if((getset >= SIOCDEVPRIVATE) &&
(getset <= (SIOCDEVPRIVATE + 15))) {
if(dev->do_ioctl==NULL)
return -EOPNOTSUPP;
ret=dev->do_ioctl(dev, &ifr, getset);
memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
break;
}
 
#ifdef CONFIG_NET_RADIO
if((getset >= SIOCIWFIRST) &&
(getset <= SIOCIWLAST))
{
if(dev->do_ioctl==NULL)
return -EOPNOTSUPP;
/* Perform the ioctl */
ret=dev->do_ioctl(dev, &ifr, getset);
/* If return args... */
if(IW_IS_GET(getset))
memcpy_tofs(arg, &ifr,
sizeof(struct ifreq));
break;
}
#endif /* CONFIG_NET_RADIO */
 
ret = -EINVAL;
}
return(ret);
/*
* The load of calls that return an ifreq and ok (saves memory).
*/
rarok:
memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
return 0;
}
 
 
/*
* This function handles all "interface"-type I/O control requests. The actual
* 'doing' part of this is dev_ifsioc above.
*/
 
int dev_ioctl(unsigned int cmd, void *arg)
{
switch(cmd)
{
case SIOCGIFCONF:
(void) dev_ifconf((char *) arg);
return 0;
 
/*
* Ioctl calls that can be done by all.
*/
case SIOCGIFFLAGS:
case SIOCGIFADDR:
case SIOCGIFDSTADDR:
case SIOCGIFBRDADDR:
case SIOCGIFNETMASK:
case SIOCGIFMETRIC:
case SIOCGIFMTU:
case SIOCGIFMEM:
case SIOCGIFHWADDR:
case SIOCGIFSLAVE:
case SIOCGIFMAP:
return dev_ifsioc(arg, cmd);
 
/*
* Ioctl calls requiring the power of a superuser
*/
case SIOCSIFFLAGS:
case SIOCSIFADDR:
case SIOCSIFDSTADDR:
case SIOCSIFBRDADDR:
case SIOCSIFNETMASK:
case SIOCSIFMETRIC:
case SIOCSIFMTU:
case SIOCSIFMEM:
case SIOCSIFHWADDR:
case SIOCSIFMAP:
case SIOCSIFSLAVE:
case SIOCADDMULTI:
case SIOCDELMULTI:
if (!suser())
return -EPERM;
return dev_ifsioc(arg, cmd);
case SIOCSIFLINK:
return -EINVAL;
 
/*
* Unknown or private ioctl.
*/
default:
if((cmd >= SIOCDEVPRIVATE) &&
(cmd <= (SIOCDEVPRIVATE + 15))) {
return dev_ifsioc(arg, cmd);
}
#ifdef CONFIG_NET_RADIO
if((cmd >= SIOCIWFIRST) &&
(cmd <= SIOCIWLAST))
{
if((IW_IS_SET(cmd)) && (!suser()))
return -EPERM;
return dev_ifsioc(arg, cmd);
}
#endif /* CONFIG_NET_RADIO */
return -EINVAL;
}
}
 
 
/*
* Initialize the DEV module. At boot time this walks the device list and
* unhooks any devices that fail to initialise (normally hardware not
* present) and leaves us with a valid list of present and active devices.
*
*/
extern int lance_init(void);
extern int pi_init(void);
extern int pt_init(void);
extern int bpq_init(void);
extern void sdla_setup(void);
extern int dlci_setup(void);
extern int sm_init(void);
extern int baycom_init(void);
 
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry pde1 = {
PROC_NET_DEV, 3, "dev",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
dev_get_info
};
#endif
 
#ifdef CONFIG_NET_RADIO
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry pde2 = {
PROC_NET_WIRELESS, 8, "wireless",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
dev_get_wireless_info
};
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_RADIO */
 
 
int net_dev_init(void)
{
struct device *dev, **dp;
 
/*
* Initialise the packet receive queue.
*/
skb_queue_head_init(&backlog);
/*
* The bridge has to be up before the devices
*/
 
#ifdef CONFIG_BRIDGE
br_init();
#endif
/*
* This is Very Ugly(tm).
*
* Some devices want to be initialized early..
*/
#if defined(CONFIG_PI)
pi_init();
#endif
#if defined(CONFIG_PT)
pt_init();
#endif
#if defined(CONFIG_BPQETHER)
bpq_init();
#endif
#if defined(CONFIG_DLCI)
dlci_setup();
#endif
#if defined(CONFIG_SDLA)
sdla_setup();
#endif
#if defined(CONFIG_BAYCOM)
baycom_init();
#endif
#if defined(CONFIG_SOUNDMODEM)
sm_init();
#endif
/*
* SLHC if present needs attaching so other people see it
* even if not opened.
*/
#if (defined(CONFIG_SLIP) && defined(CONFIG_SLIP_COMPRESSED)) \
|| defined(CONFIG_PPP) \
|| (defined(CONFIG_ISDN) && defined(CONFIG_ISDN_PPP))
slhc_install();
#endif
 
/*
* Add the devices.
* If the call to dev->init fails, the dev is removed
* from the chain disconnecting the device until the
* next reboot.
*/
 
dp = &dev_base;
while ((dev = *dp) != NULL)
{
int i;
for (i = 0; i < DEV_NUMBUFFS; i++) {
skb_queue_head_init(dev->buffs + i);
}
 
if (dev->init && dev->init(dev))
{
/*
* It failed to come up. Unhook it.
*/
*dp = dev->next;
}
else
{
dp = &dev->next;
}
}
 
#ifdef CONFIG_PROC_FS
proc_net_register(&pde1);
#endif
 
#ifdef CONFIG_NET_RADIO
#ifdef CONFIG_PROC_FS
proc_net_register(&pde2);
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_RADIO */
 
/*
* Initialise net_alias engine
*
* - register net_alias device notifier
* - register proc entries: /proc/net/alias_types
* /proc/net/aliases
*/
 
#ifdef CONFIG_NET_ALIAS
net_alias_init();
#endif
 
init_bh(NET_BH, net_bh);
return 0;
}
/trunk/uclinux/uClinux-2.0.x/net/core/skbuff.c
0,0 → 1,936
/*
* Routines having to do with the 'struct sk_buff' memory handlers.
*
* Authors: Alan Cox <iiitac@pyr.swan.ac.uk>
* Florian La Roche <rzsfl@rz.uni-sb.de>
*
* Fixes:
* Alan Cox : Fixed the worst of the load balancer bugs.
* Dave Platt : Interrupt stacking fix.
* Richard Kooijman : Timestamp fixes.
* Alan Cox : Changed buffer format.
* Alan Cox : destructor hook for AF_UNIX etc.
* Linus Torvalds : Better skb_clone.
* Alan Cox : Added skb_copy.
* Alan Cox : Added all the changed routines Linus
* only put in the headers
* Ray VanTassle : Fixed --skb->lock in free
*
* TO FIX:
* The __skb_ routines ought to check interrupts are disabled
* when called, and bitch like crazy if not. Unfortunately I don't think
* we currently have a portable way to check if interrupts are off -
* Linus ???
*
* 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.
*/
 
/*
* The functions in this file will not compile correctly with gcc 2.4.x
*/
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/skbuff.h>
 
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/sock.h>
 
#include <asm/segment.h>
#include <asm/system.h>
 
/*
* Resource tracking variables
*/
 
atomic_t net_skbcount = 0;
atomic_t net_locked = 0;
atomic_t net_allocs = 0;
atomic_t net_fails = 0;
atomic_t net_free_locked = 0;
 
extern atomic_t ip_frag_mem;
 
#undef TRACK_SKB_USAGE
 
void show_net_buffers(void)
{
printk(KERN_INFO "Networking buffers in use : %u\n",net_skbcount);
printk(KERN_INFO "Network buffers locked by drivers : %u\n",net_locked);
printk(KERN_INFO "Total network buffer allocations : %u\n",net_allocs);
printk(KERN_INFO "Total failed network buffer allocs : %u\n",net_fails);
printk(KERN_INFO "Total free while locked events : %u\n",net_free_locked);
#ifdef CONFIG_INET
printk(KERN_INFO "IP fragment buffer size : %u\n",ip_frag_mem);
#endif
}
 
#if CONFIG_SKB_CHECK
 
/*
* Debugging paranoia. Can go later when this crud stack works
*/
 
int skb_check(struct sk_buff *skb, int head, int line, char *file)
{
if (head) {
if (skb->magic_debug_cookie != SK_HEAD_SKB) {
printk("File: %s Line %d, found a bad skb-head\n",
file,line);
return -1;
}
if (!skb->next || !skb->prev) {
printk("skb_check: head without next or prev\n");
return -1;
}
if (skb->next->magic_debug_cookie != SK_HEAD_SKB
&& skb->next->magic_debug_cookie != SK_GOOD_SKB) {
printk("File: %s Line %d, bad next head-skb member\n",
file,line);
return -1;
}
if (skb->prev->magic_debug_cookie != SK_HEAD_SKB
&& skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
printk("File: %s Line %d, bad prev head-skb member\n",
file,line);
return -1;
}
#if 0
{
struct sk_buff *skb2 = skb->next;
int i = 0;
while (skb2 != skb && i < 5) {
if (skb_check(skb2, 0, line, file) < 0) {
printk("bad queue element in whole queue\n");
return -1;
}
i++;
skb2 = skb2->next;
}
}
#endif
return 0;
}
if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB
&& skb->next->magic_debug_cookie != SK_GOOD_SKB) {
printk("File: %s Line %d, bad next skb member\n",
file,line);
return -1;
}
if (skb->prev != NULL && skb->prev->magic_debug_cookie != SK_HEAD_SKB
&& skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
printk("File: %s Line %d, bad prev skb member\n",
file,line);
return -1;
}
 
 
if(skb->magic_debug_cookie==SK_FREED_SKB)
{
printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n",
file,line);
printk("skb=%p, real size=%d, free=%d\n",
skb,skb->truesize,skb->free);
return -1;
}
if(skb->magic_debug_cookie!=SK_GOOD_SKB)
{
printk("File: %s Line %d, passed a non skb!\n", file,line);
printk("skb=%p, real size=%d, free=%d\n",
skb,skb->truesize,skb->free);
return -1;
}
if(skb->head>skb->data)
{
printk("File: %s Line %d, head > data !\n", file,line);
printk("skb=%p, head=%p, data=%p\n",
skb,skb->head,skb->data);
return -1;
}
if(skb->tail>skb->end)
{
printk("File: %s Line %d, tail > end!\n", file,line);
printk("skb=%p, tail=%p, end=%p\n",
skb,skb->tail,skb->end);
return -1;
}
if(skb->data>skb->tail)
{
printk("File: %s Line %d, data > tail!\n", file,line);
printk("skb=%p, data=%p, tail=%p\n",
skb,skb->data,skb->tail);
return -1;
}
if(skb->tail-skb->data!=skb->len)
{
printk("File: %s Line %d, wrong length\n", file,line);
printk("skb=%p, data=%p, end=%p len=%ld\n",
skb,skb->data,skb->end,skb->len);
return -1;
}
if((unsigned long) skb->end > (unsigned long) skb)
{
printk("File: %s Line %d, control overrun\n", file,line);
printk("skb=%p, end=%p\n",
skb,skb->end);
return -1;
}
 
/* Guess it might be acceptable then */
return 0;
}
#endif
 
 
#if CONFIG_SKB_CHECK
void skb_queue_head_init(struct sk_buff_head *list)
{
list->prev = (struct sk_buff *)list;
list->next = (struct sk_buff *)list;
list->qlen = 0;
list->magic_debug_cookie = SK_HEAD_SKB;
}
 
 
/*
* Insert an sk_buff at the start of a list.
*/
void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
{
unsigned long flags;
struct sk_buff *list = (struct sk_buff *)list_;
 
save_flags(flags);
cli();
 
IS_SKB(newsk);
IS_SKB_HEAD(list);
if (newsk->next || newsk->prev)
printk("Suspicious queue head: sk_buff on list!\n");
 
newsk->next = list->next;
newsk->prev = list;
 
newsk->next->prev = newsk;
newsk->prev->next = newsk;
newsk->list = list_;
list_->qlen++;
 
restore_flags(flags);
}
 
void __skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
{
struct sk_buff *list = (struct sk_buff *)list_;
 
 
IS_SKB(newsk);
IS_SKB_HEAD(list);
if (newsk->next || newsk->prev)
printk("Suspicious queue head: sk_buff on list!\n");
 
newsk->next = list->next;
newsk->prev = list;
 
newsk->next->prev = newsk;
newsk->prev->next = newsk;
newsk->list = list_;
list_->qlen++;
 
}
 
/*
* Insert an sk_buff at the end of a list.
*/
void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
{
unsigned long flags;
struct sk_buff *list = (struct sk_buff *)list_;
 
save_flags(flags);
cli();
 
if (newsk->next || newsk->prev)
printk("Suspicious queue tail: sk_buff on list!\n");
IS_SKB(newsk);
IS_SKB_HEAD(list);
 
newsk->next = list;
newsk->prev = list->prev;
 
newsk->next->prev = newsk;
newsk->prev->next = newsk;
newsk->list = list_;
list_->qlen++;
 
restore_flags(flags);
}
 
void __skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
{
struct sk_buff *list = (struct sk_buff *)list_;
 
if (newsk->next || newsk->prev)
printk("Suspicious queue tail: sk_buff on list!\n");
IS_SKB(newsk);
IS_SKB_HEAD(list);
 
newsk->next = list;
newsk->prev = list->prev;
 
newsk->next->prev = newsk;
newsk->prev->next = newsk;
newsk->list = list_;
list_->qlen++;
}
 
/*
* Remove an sk_buff from a list. This routine is also interrupt safe
* so you can grab read and free buffers as another process adds them.
*/
 
struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
{
unsigned long flags;
struct sk_buff *result;
struct sk_buff *list = (struct sk_buff *)list_;
 
save_flags(flags);
cli();
 
IS_SKB_HEAD(list);
 
result = list->next;
if (result == list) {
restore_flags(flags);
return NULL;
}
 
result->next->prev = list;
list->next = result->next;
 
result->next = NULL;
result->prev = NULL;
list_->qlen--;
result->list = NULL;
restore_flags(flags);
 
IS_SKB(result);
return result;
}
 
struct sk_buff *__skb_dequeue(struct sk_buff_head *list_)
{
struct sk_buff *result;
struct sk_buff *list = (struct sk_buff *)list_;
 
IS_SKB_HEAD(list);
 
result = list->next;
if (result == list) {
return NULL;
}
 
result->next->prev = list;
list->next = result->next;
 
result->next = NULL;
result->prev = NULL;
list_->qlen--;
result->list = NULL;
IS_SKB(result);
return result;
}
 
/*
* Insert a packet before another one in a list.
*/
void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
 
IS_SKB(old);
IS_SKB(newsk);
 
if(!old->next || !old->prev)
printk("insert before unlisted item!\n");
if(newsk->next || newsk->prev)
printk("inserted item is already on a list.\n");
 
save_flags(flags);
cli();
newsk->next = old;
newsk->prev = old->prev;
old->prev = newsk;
newsk->prev->next = newsk;
newsk->list = old->list;
newsk->list->qlen++;
 
restore_flags(flags);
}
 
/*
* Insert a packet before another one in a list.
*/
 
void __skb_insert(struct sk_buff *newsk,
struct sk_buff * prev, struct sk_buff *next,
struct sk_buff_head * list)
{
IS_SKB(prev);
IS_SKB(newsk);
IS_SKB(next);
 
if(!prev->next || !prev->prev)
printk("insert after unlisted item!\n");
if(!next->next || !next->prev)
printk("insert before unlisted item!\n");
if(newsk->next || newsk->prev)
printk("inserted item is already on a list.\n");
 
newsk->next = next;
newsk->prev = prev;
next->prev = newsk;
prev->next = newsk;
newsk->list = list;
list->qlen++;
 
}
 
/*
* Place a packet after a given packet in a list.
*/
void skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
 
IS_SKB(old);
IS_SKB(newsk);
 
if(!old->next || !old->prev)
printk("append before unlisted item!\n");
if(newsk->next || newsk->prev)
printk("append item is already on a list.\n");
 
save_flags(flags);
cli();
 
newsk->prev = old;
newsk->next = old->next;
newsk->next->prev = newsk;
old->next = newsk;
newsk->list = old->list;
newsk->list->qlen++;
 
restore_flags(flags);
}
 
/*
* Remove an sk_buff from its list. Works even without knowing the list it
* is sitting on, which can be handy at times. It also means that THE LIST
* MUST EXIST when you unlink. Thus a list must have its contents unlinked
* _FIRST_.
*/
void skb_unlink(struct sk_buff *skb)
{
unsigned long flags;
 
save_flags(flags);
cli();
 
IS_SKB(skb);
 
if(skb->list)
{
skb->list->qlen--;
skb->next->prev = skb->prev;
skb->prev->next = skb->next;
skb->next = NULL;
skb->prev = NULL;
skb->list = NULL;
}
#ifdef PARANOID_BUGHUNT_MODE /* This is legal but we sometimes want to watch it */
else
printk("skb_unlink: not a linked element\n");
#endif
restore_flags(flags);
}
 
void __skb_unlink(struct sk_buff *skb)
{
IS_SKB(skb);
 
if(skb->list)
{
skb->list->qlen--;
skb->next->prev = skb->prev;
skb->prev->next = skb->next;
skb->next = NULL;
skb->prev = NULL;
skb->list = NULL;
}
#ifdef PARANOID_BUGHUNT_MODE /* This is legal but we sometimes want to watch it */
else
printk("skb_unlink: not a linked element\n");
#endif
}
 
/*
* Add data to an sk_buff
*/
unsigned char *skb_put(struct sk_buff *skb, int len)
{
unsigned char *tmp=skb->tail;
IS_SKB(skb);
skb->tail+=len;
skb->len+=len;
IS_SKB(skb);
if(skb->tail>skb->end)
panic("skput:over: %p:%d", __builtin_return_address(0),len);
return tmp;
}
 
unsigned char *skb_push(struct sk_buff *skb, int len)
{
IS_SKB(skb);
skb->data-=len;
skb->len+=len;
IS_SKB(skb);
if(skb->data<skb->head)
panic("skpush:under: %p:%d", __builtin_return_address(0),len);
return skb->data;
}
 
unsigned char * skb_pull(struct sk_buff *skb, int len)
{
IS_SKB(skb);
if(len>skb->len)
return 0;
skb->data+=len;
skb->len-=len;
return skb->data;
}
 
int skb_headroom(struct sk_buff *skb)
{
IS_SKB(skb);
return skb->data-skb->head;
}
 
int skb_tailroom(struct sk_buff *skb)
{
IS_SKB(skb);
return skb->end-skb->tail;
}
 
void skb_reserve(struct sk_buff *skb, int len)
{
IS_SKB(skb);
skb->data+=len;
skb->tail+=len;
if(skb->tail>skb->end)
panic("sk_res: over");
if(skb->data<skb->head)
panic("sk_res: under");
IS_SKB(skb);
}
 
void skb_trim(struct sk_buff *skb, int len)
{
IS_SKB(skb);
if(skb->len>len)
{
skb->len=len;
skb->tail=skb->data+len;
}
}
 
 
 
#endif
 
/*
* Free an sk_buff. This still knows about things it should
* not need to like protocols and sockets.
*/
 
void kfree_skb(struct sk_buff *skb, int rw)
{
if (skb == NULL)
{
printk(KERN_CRIT "kfree_skb: skb = NULL (from %p)\n",
__builtin_return_address(0));
return;
}
#if CONFIG_SKB_CHECK
IS_SKB(skb);
#endif
/* Check it twice, this is such a rare event and only occurs under
* extremely high load, normal code path should not suffer from the
* overhead of the cli.
*/
if (skb->lock) {
unsigned long flags;
 
save_flags(flags); cli();
if(skb->lock) {
skb->free = 3; /* Free when unlocked */
net_free_locked++;
restore_flags(flags);
return;
}
restore_flags(flags);
}
 
if (skb->free == 2)
printk(KERN_WARNING "Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p)\n",
__builtin_return_address(0));
if (skb->list)
printk(KERN_WARNING "Warning: kfree_skb passed an skb still on a list (from %p).\n",
__builtin_return_address(0));
 
if(skb->destructor)
skb->destructor(skb);
if (skb->sk)
{
struct sock * sk = skb->sk;
if(sk->prot!=NULL)
{
if (rw)
sock_rfree(sk, skb);
else
sock_wfree(sk, skb);
 
}
else
{
if (rw)
atomic_sub(skb->truesize, &sk->rmem_alloc);
else {
if(!sk->dead)
sk->write_space(sk);
atomic_sub(skb->truesize, &sk->wmem_alloc);
}
kfree_skbmem(skb);
}
}
else
kfree_skbmem(skb);
}
 
#ifdef TRACK_SKB_USAGE
int skbtotal = 0;
#endif
 
/*
* Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
* fields and also do memory statistics to find all the [BEEP] leaks.
*/
struct sk_buff *alloc_skb(unsigned int size,int priority)
{
struct sk_buff *skb;
int len=size;
unsigned char *bptr;
 
if (intr_count && priority!=GFP_ATOMIC)
{
static int count = 0;
if (++count < 5) {
printk(KERN_ERR "alloc_skb called nonatomically from interrupt %p\n",
__builtin_return_address(0));
priority = GFP_ATOMIC;
}
}
 
size=(size+15)&~15; /* Allow for alignments. Make a multiple of 16 bytes */
size+=sizeof(struct sk_buff); /* And stick the control itself on the end */
/*
* Allocate some space
*/
bptr=(unsigned char *)kmalloc(size,priority);
if (bptr == NULL)
{
#ifdef TRACK_SKB_USAGE
printk("Failed to allocate %d byte skb\n", size);
#endif
net_fails++;
return NULL;
}
#ifdef TRACK_SKB_USAGE
skbtotal += size;
printk("Allocated %d byte skb, for %d total\n", size, skbtotal);
#endif
#ifdef PARANOID_BUGHUNT_MODE
if(skb->magic_debug_cookie == SK_GOOD_SKB)
printk("Kernel kmalloc handed us an existing skb (%p)\n",skb);
#endif
/*
* Now we play a little game with the caches. Linux kmalloc is
* a bit cache dumb, in fact its just about maximally non
* optimal for typical kernel buffers. We actually run faster
* by doing the following. Which is to deliberately put the
* skb at the _end_ not the start of the memory block.
*/
net_allocs++;
skb=(struct sk_buff *)(bptr+size)-1;
 
skb->count = 1; /* only one reference to this */
skb->data_skb = NULL; /* and we're our own data skb */
 
skb->free = 2; /* Invalid so we pick up forgetful users */
skb->lock = 0;
skb->pkt_type = PACKET_HOST; /* Default type */
skb->pkt_bridged = 0; /* Not bridged */
skb->prev = skb->next = skb->link3 = NULL;
skb->list = NULL;
skb->sk = NULL;
skb->truesize=size;
skb->localroute=0;
skb->stamp.tv_sec=0; /* No idea about time */
skb->localroute = 0;
skb->ip_summed = 0;
memset(skb->proto_priv, 0, sizeof(skb->proto_priv));
net_skbcount++;
#if CONFIG_SKB_CHECK
skb->magic_debug_cookie = SK_GOOD_SKB;
#endif
skb->users = 0;
/* Load the data pointers */
skb->head=bptr;
skb->data=bptr;
skb->tail=bptr;
skb->end=bptr+len;
skb->len=0;
skb->destructor=NULL;
return skb;
}
 
/*
* Free an skbuff by memory
*/
 
static inline void __kfree_skbmem(struct sk_buff *skb)
{
/* don't do anything if somebody still uses us */
if (atomic_dec_and_test(&skb->count)) {
kfree(skb->head);
atomic_dec(&net_skbcount);
}
}
 
void kfree_skbmem(struct sk_buff *skb)
{
void * addr = skb->head;
 
/* don't do anything if somebody still uses us */
if (atomic_dec_and_test(&skb->count)) {
/* free the skb that contains the actual data if we've clone()'d */
if (skb->data_skb) {
#ifdef TRACK_SKB_USAGE
skbtotal -= skb->truesize;
printk("Deallocation %d byte skb clone, for %d total\n", skb->truesize, skbtotal);
#endif
addr = skb;
__kfree_skbmem(skb->data_skb);
}
#ifdef TRACK_SKB_USAGE
skbtotal -= skb->truesize;
printk("Deallocation %d byte skb, for %d total\n", skb->truesize, skbtotal);
#endif
kfree(addr);
atomic_dec(&net_skbcount);
}
}
 
/*
* Duplicate an sk_buff. The new one is not owned by a socket or locked
* and will be freed on deletion.
*/
 
struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
{
struct sk_buff *n;
 
IS_SKB(skb);
n = kmalloc(sizeof(*n), priority);
if (!n)
return NULL;
memcpy(n, skb, sizeof(*n));
n->count = 1;
if (skb->data_skb)
skb = skb->data_skb;
atomic_inc(&skb->count);
atomic_inc(&net_allocs);
atomic_inc(&net_skbcount);
n->data_skb = skb;
n->next = n->prev = n->link3 = NULL;
n->list = NULL;
n->sk = NULL;
n->free = 1;
n->tries = 0;
n->lock = 0;
n->users = 0;
#ifdef TRACK_SKB_USAGE
skbtotal += skb->truesize;
printk("Allocation %d byte skb clone, for %d total\n", skb->truesize, skbtotal);
#endif
return n;
}
 
/*
* This is slower, and copies the whole data area
*/
struct sk_buff *skb_copy(struct sk_buff *skb, int priority)
{
struct sk_buff *n;
unsigned long offset;
 
/*
* Allocate the copy buffer
*/
IS_SKB(skb);
n=alloc_skb(skb->truesize-sizeof(struct sk_buff),priority);
if(n==NULL)
return NULL;
 
/*
* Shift between the two data areas in bytes
*/
offset=n->head-skb->head;
 
/* Set the data pointer */
skb_reserve(n,skb->data-skb->head);
/* Set the tail pointer and length */
skb_put(n,skb->len);
/* Copy the bytes */
memcpy(n->head,skb->head,skb->end-skb->head);
n->link3=NULL;
n->list=NULL;
n->sk=NULL;
n->when=skb->when;
n->dev=skb->dev;
n->h.raw=skb->h.raw+offset;
n->mac.raw=skb->mac.raw+offset;
n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
n->saddr=skb->saddr;
n->daddr=skb->daddr;
n->raddr=skb->raddr;
n->seq=skb->seq;
n->end_seq=skb->end_seq;
n->ack_seq=skb->ack_seq;
n->acked=skb->acked;
memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
n->used=skb->used;
n->free=1;
n->arp=skb->arp;
n->tries=0;
n->lock=0;
n->users=0;
n->pkt_type=skb->pkt_type;
n->stamp=skb->stamp;
IS_SKB(n);
return n;
}
 
/*
* Skbuff device locking
*/
 
void skb_device_lock(struct sk_buff *skb)
{
unsigned long flags;
 
save_flags(flags); cli();
if(skb->lock)
printk("double lock on device queue, lock=%d caller=%p\n",
skb->lock, (&skb)[-1]);
else
net_locked++;
skb->lock++;
restore_flags(flags);
}
 
void skb_device_unlock(struct sk_buff *skb)
{
unsigned long flags;
 
save_flags(flags); cli();
if(skb->lock==0)
printk("double unlock on device queue!\n");
skb->lock--;
if(skb->lock==0)
net_locked--;
restore_flags(flags);
 
if (skb->free == 3) {
skb->free = 1;
kfree_skb(skb, FREE_WRITE);
}
}
 
void dev_kfree_skb(struct sk_buff *skb, int mode)
{
unsigned long flags;
 
save_flags(flags);
cli();
if(skb->lock)
{
net_locked--;
skb->lock--;
}
if (!skb->lock && (skb->free == 1 || skb->free == 3))
{
restore_flags(flags);
kfree_skb(skb,mode);
}
else
restore_flags(flags);
}
 
struct sk_buff *dev_alloc_skb(unsigned int length)
{
struct sk_buff *skb;
 
skb = alloc_skb(length+16, GFP_ATOMIC);
if (skb)
skb_reserve(skb,16);
return skb;
}
 
int skb_device_locked(struct sk_buff *skb)
{
return skb->lock? 1 : 0;
}
/trunk/uclinux/uClinux-2.0.x/net/core/datagram.c
0,0 → 1,239
/*
* SUCS NET3:
*
* Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
* of these would make sense. Not tonight however 8-).
* This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical select code and mostly
* identical recvmsg() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
*
* Authors: Alan Cox <alan@cymru.net>. (datagram_select() from old udp.c code)
*
* Fixes:
* Alan Cox : NULL return from skb_peek_copy() understood
* Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff.
* Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
* AX.25 now works right, and SPX is feasible.
* Alan Cox : Fixed write select of non IP protocol crash.
* Florian La Roche: Changed for my new skbuff handling.
* Darryl Miles : Fixed non-blocking SOCK_SEQPACKET.
* Linus Torvalds : BSD semantic fixes.
* Alan Cox : Datagram iovec handling
* Darryl Miles : Fixed non-blocking SOCK_STREAM.
*
*/
 
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
 
 
/*
* Wait for a packet..
*
* Interrupts off so that no packet arrives before we begin sleeping.
* Otherwise we might miss our wake up
*/
 
static inline void wait_for_packet(struct sock * sk)
{
unsigned long flags;
 
release_sock(sk);
save_flags(flags);
cli();
if (skb_peek(&sk->receive_queue) == NULL)
interruptible_sleep_on(sk->sleep);
restore_flags(flags);
lock_sock(sk);
}
 
/*
* Is a socket 'connection oriented' ?
*/
static inline int connection_based(struct sock *sk)
{
if(sk->type==SOCK_SEQPACKET || sk->type==SOCK_STREAM)
return 1;
return 0;
}
 
/*
* Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible
* races. This replaces identical code in packet,raw and udp, as well as the IPX
* AX.25 and Appletalk. It also finally fixes the long standing peek and read
* race for datagram sockets. If you alter this routine remember it must be
* re-entrant.
*
* This function will lock the socket if a skb is returned, so the caller
* needs to unlock the socket in that case (usually by calling skb_free_datagram)
*/
 
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err)
{
int error;
struct sk_buff *skb;
 
lock_sock(sk);
restart:
while(skb_queue_empty(&sk->receive_queue)) /* No data */
{
/* Socket errors? */
error = sock_error(sk);
if (error)
goto no_packet;
 
/* Socket shut down? */
if (sk->shutdown & RCV_SHUTDOWN)
goto no_packet;
 
/* Sequenced packets can come disconnected. If so we report the problem */
error = -ENOTCONN;
if(connection_based(sk) && sk->state!=TCP_ESTABLISHED)
goto no_packet;
 
/* User doesn't want to wait */
error = -EAGAIN;
if (noblock)
goto no_packet;
 
/* handle signals */
error = -ERESTARTSYS;
if (current->signal & ~current->blocked)
goto no_packet;
 
wait_for_packet(sk);
}
 
/* Again only user level code calls this function, so nothing interrupt level
will suddenly eat the receive_queue */
if (flags & MSG_PEEK)
{
unsigned long flags;
save_flags(flags);
cli();
skb=skb_peek(&sk->receive_queue);
if(skb!=NULL)
skb->users++;
restore_flags(flags);
if(skb==NULL) /* shouldn't happen but .. */
goto restart;
return skb;
}
skb = skb_dequeue(&sk->receive_queue);
if (!skb) /* Avoid race if someone beats us to the data */
goto restart;
skb->users++;
return skb;
 
no_packet:
release_sock(sk);
*err = error;
return NULL;
}
 
void skb_free_datagram(struct sock * sk, struct sk_buff *skb)
{
unsigned long flags;
 
save_flags(flags);
cli();
skb->users--;
if(skb->users <= 0) {
/* See if it needs destroying */
/* Been dequeued by someone - ie it's read */
if(!skb->next && !skb->prev)
kfree_skb(skb,FREE_READ);
}
restore_flags(flags);
release_sock(sk);
}
 
/*
* Copy a datagram to a linear buffer.
*/
 
void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
{
memcpy_tofs(to,skb->h.raw+offset,size);
}
 
 
/*
* Copy a datagram to an iovec.
*/
void skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to, int size)
{
memcpy_toiovec(to,skb->h.raw+offset,size);
}
 
/*
* Datagram select: Again totally generic. Moved from udp.c
* Now does seqpacket.
*/
 
int datagram_select(struct sock *sk, int sel_type, select_table *wait)
{
select_wait(sk->sleep, wait);
switch(sel_type)
{
case SEL_IN:
if (sk->err)
return 1;
if (sk->shutdown & RCV_SHUTDOWN)
return 1;
if (connection_based(sk) && sk->state==TCP_CLOSE)
{
/* Connection closed: Wake up */
return(1);
}
if (skb_peek(&sk->receive_queue) != NULL)
{ /* This appears to be consistent
with other stacks */
return(1);
}
return(0);
 
case SEL_OUT:
if (sk->err)
return 1;
if (sk->shutdown & SEND_SHUTDOWN)
return 1;
if (connection_based(sk) && sk->state==TCP_SYN_SENT)
{
/* Connection still in progress */
break;
}
if (sk->prot && sock_wspace(sk) >= MIN_WRITE_SPACE)
{
return(1);
}
if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE)
{
return(1);
}
return(0);
 
case SEL_EX:
if (sk->err)
return(1); /* Socket has gone into error state (eg icmp error) */
return(0);
}
return(0);
}
/trunk/uclinux/uClinux-2.0.x/net/core/iovec.o
0,0 → 1,4
 
 
+l t.filegiovec.c.text.data.bss%3hCUj_memcpywgcc2_compiled.___gnu_compiled_c_verify_iovec_memcpy_toiovec_memcpy_fromiovec_move_addr_to_kernel_verify_area \ No newline at end of file
/trunk/uclinux/uClinux-2.0.x/net/core/skbuff.o
0,0 → 1,82
+skput:over: %p:%dskpush:under: %p:%d<6>Networking buffers in use : %u +<6>Network buffers locked by drivers : %u +<6>Total network buffer allocations : %u +<6>Total failed network buffer allocs : %u +<6>Total free while locked events : %u +!H&`c cR`c c~`c c`c  c`c c!HH! <2>kfree_skb: skb = NULL (from %p) +<4>Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p) +<4>Warning: kfree_skb passed an skb still on a list (from %p). +!HP `p@.P +a, pne#P a$ne#P `ba$ Za$nbcc a, n#` a,- $` H nN*` 6j#` 0` j.*0` pa Jjc` +a jHcc#`H jpaJ jc` +an|n!A HH!(<3>alloc_skb called nonatomically from interrupt %p +!HP `pC`c$( &` `c(  a jC Jj`& /fP cl b t e g h f iPp x       P T X \  jc0| 0 0p0  dc`!A HH!!HP`p |a(Ntjc# +a(, /x@R, a$Ltjc# +l|a rca tarca!A AHH!,!HP`p p@,Pl8`` tnx#P aNtjc +aa@Ja + a@Ja + ` b` d el px      j!A HH! !H P`plpclK`* \`|lc8|j0 +( + 08@c@ + D( + +0`c`c @|j| ` + + +l +l + l$cp +$(p + (l,cp +,l8 +8l< +
/trunk/uclinux/uClinux-2.0.x/net/core/datagram.o
0,0 → 1,11
 
+jc +P` + a,.ynnjc jlDc#  Dl`P!A A A$(HH!
/trunk/uclinux/uClinux-2.0.x/net/core/Makefile
0,0 → 1,31
#
# Makefile for the Linux TCP/IP (INET) layer.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now in the main makefile...
 
O_TARGET := core.o
 
O_OBJS := sock.o skbuff.o iovec.o datagram.o sysctl_net_core.o
 
ifdef CONFIG_NET
 
O_OBJS += dev.o dev_mcast.o
 
ifdef CONFIG_FIREWALL
OX_OBJS += firewall.o
endif
 
ifdef CONFIG_NET_ALIAS
O_OBJS += net_alias.o
endif
 
endif
 
include $(TOPDIR)/Rules.make
 
tar:
tar -cvf /dev/f1 .

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.