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 G e pc H p @ mL5 kLBl UC J g` dL<