2005-04-17 02:20:36 +04:00
/*
* Generic address resultion entity
*
* Authors :
* net_random Alan Cox
* net_ratelimit Andy Kleen
2006-06-25 18:54:55 +04:00
* in { 4 , 6 } _pton YOSHIFUJI Hideaki , Copyright ( C ) 2006 USAGI / WIDE Project
2005-04-17 02:20:36 +04:00
*
* Created by Alexey Kuznetsov < kuznet @ ms2 . inr . ac . ru >
*
* 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/module.h>
# include <linux/jiffies.h>
# include <linux/kernel.h>
2005-08-16 09:18:02 +04:00
# include <linux/inet.h>
2005-04-17 02:20:36 +04:00
# include <linux/mm.h>
2005-08-16 09:18:02 +04:00
# include <linux/net.h>
2005-04-17 02:20:36 +04:00
# include <linux/string.h>
# include <linux/types.h>
# include <linux/random.h>
# include <linux/percpu.h>
# include <linux/init.h>
2005-07-28 02:24:42 +04:00
# include <asm/byteorder.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/uaccess.h>
/*
This is a maximally equidistributed combined Tausworthe generator
based on code from GNU Scientific Library 1.5 ( 30 Jun 2004 )
x_n = ( s1_n ^ s2_n ^ s3_n )
s1_ { n + 1 } = ( ( ( s1_n & 4294967294 ) < < 12 ) ^ ( ( ( s1_n < < 13 ) ^ s1_n ) > > 19 ) )
s2_ { n + 1 } = ( ( ( s2_n & 4294967288 ) < < 4 ) ^ ( ( ( s2_n < < 2 ) ^ s2_n ) > > 25 ) )
s3_ { n + 1 } = ( ( ( s3_n & 4294967280 ) < < 17 ) ^ ( ( ( s3_n < < 3 ) ^ s3_n ) > > 11 ) )
The period of this generator is about 2 ^ 88.
From : P . L ' Ecuyer , " Maximally Equidistributed Combined Tausworthe
Generators " , Mathematics of Computation, 65, 213 (1996), 203--213.
This is available on the net from L ' Ecuyer ' s home page ,
http : //www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme.ps
ftp : //ftp.iro.umontreal.ca/pub/simulation/lecuyer/papers/tausme.ps
There is an erratum in the paper " Tables of Maximally
Equidistributed Combined LFSR Generators " , Mathematics of
Computation , 68 , 225 ( 1999 ) , 261 - - 269 :
http : //www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps
. . . the k_j most significant bits of z_j must be non -
zero , for each j . ( Note : this restriction also applies to the
computer code given in [ 4 ] , but was mistakenly not mentioned in
that paper . )
This affects the seeding procedure by imposing the requirement
s1 > 1 , s2 > 7 , s3 > 15.
*/
struct nrnd_state {
u32 s1 , s2 , s3 ;
} ;
static DEFINE_PER_CPU ( struct nrnd_state , net_rand_state ) ;
static u32 __net_random ( struct nrnd_state * state )
{
# define TAUSWORTHE(s,a,b,c,d) ((s&c)<<d) ^ (((s <<a) ^ s)>>b)
state - > s1 = TAUSWORTHE ( state - > s1 , 13 , 19 , 4294967294UL , 12 ) ;
state - > s2 = TAUSWORTHE ( state - > s2 , 2 , 25 , 4294967288UL , 4 ) ;
state - > s3 = TAUSWORTHE ( state - > s3 , 3 , 11 , 4294967280UL , 17 ) ;
return ( state - > s1 ^ state - > s2 ^ state - > s3 ) ;
}
static void __net_srandom ( struct nrnd_state * state , unsigned long s )
{
if ( s = = 0 )
s = 1 ; /* default seed is 1 */
# define LCG(n) (69069 * n)
state - > s1 = LCG ( s ) ;
state - > s2 = LCG ( state - > s1 ) ;
state - > s3 = LCG ( state - > s2 ) ;
/* "warm it up" */
__net_random ( state ) ;
__net_random ( state ) ;
__net_random ( state ) ;
__net_random ( state ) ;
__net_random ( state ) ;
__net_random ( state ) ;
}
unsigned long net_random ( void )
{
unsigned long r ;
struct nrnd_state * state = & get_cpu_var ( net_rand_state ) ;
r = __net_random ( state ) ;
put_cpu_var ( state ) ;
return r ;
}
void net_srandom ( unsigned long entropy )
{
struct nrnd_state * state = & get_cpu_var ( net_rand_state ) ;
__net_srandom ( state , state - > s1 ^ entropy ) ;
put_cpu_var ( state ) ;
}
void __init net_random_init ( void )
{
int i ;
2006-04-11 09:52:50 +04:00
for_each_possible_cpu ( i ) {
2005-04-17 02:20:36 +04:00
struct nrnd_state * state = & per_cpu ( net_rand_state , i ) ;
__net_srandom ( state , i + jiffies ) ;
}
}
static int net_random_reseed ( void )
{
int i ;
2006-08-15 11:03:01 +04:00
unsigned long seed ;
2005-04-17 02:20:36 +04:00
2006-04-11 09:52:50 +04:00
for_each_possible_cpu ( i ) {
2005-04-17 02:20:36 +04:00
struct nrnd_state * state = & per_cpu ( net_rand_state , i ) ;
2006-08-15 11:03:01 +04:00
get_random_bytes ( & seed , sizeof ( seed ) ) ;
__net_srandom ( state , seed ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
late_initcall ( net_random_reseed ) ;
int net_msg_cost = 5 * HZ ;
int net_msg_burst = 10 ;
/*
* All net warning printk ( ) s should be guarded by this function .
*/
int net_ratelimit ( void )
{
return __printk_ratelimit ( net_msg_cost , net_msg_burst ) ;
}
EXPORT_SYMBOL ( net_random ) ;
EXPORT_SYMBOL ( net_ratelimit ) ;
EXPORT_SYMBOL ( net_srandom ) ;
2005-07-28 02:24:42 +04:00
/*
* Convert an ASCII string to binary IP .
* This is outside of net / ipv4 / because various code that uses IP addresses
* is otherwise not dependent on the TCP / IP stack .
*/
2006-01-07 00:24:54 +03:00
__be32 in_aton ( const char * str )
2005-07-28 02:24:42 +04:00
{
unsigned long l ;
unsigned int val ;
int i ;
l = 0 ;
for ( i = 0 ; i < 4 ; i + + )
{
l < < = 8 ;
if ( * str ! = ' \0 ' )
{
val = 0 ;
2005-11-09 21:34:01 +03:00
while ( * str ! = ' \0 ' & & * str ! = ' . ' & & * str ! = ' \n ' )
2005-07-28 02:24:42 +04:00
{
val * = 10 ;
val + = * str - ' 0 ' ;
str + + ;
}
l | = val ;
if ( * str ! = ' \0 ' )
str + + ;
}
}
return ( htonl ( l ) ) ;
}
EXPORT_SYMBOL ( in_aton ) ;
2006-06-25 18:54:55 +04:00
# define IN6PTON_XDIGIT 0x00010000
# define IN6PTON_DIGIT 0x00020000
# define IN6PTON_COLON_MASK 0x00700000
# define IN6PTON_COLON_1 0x00100000 /* single : requested */
# define IN6PTON_COLON_2 0x00200000 /* second : requested */
# define IN6PTON_COLON_1_2 0x00400000 /* :: requested */
# define IN6PTON_DOT 0x00800000 /* . */
# define IN6PTON_DELIM 0x10000000
# define IN6PTON_NULL 0x20000000 /* first/tail */
# define IN6PTON_UNKNOWN 0x40000000
static inline int digit2bin ( char c , char delim )
{
if ( c = = delim | | c = = ' \0 ' )
return IN6PTON_DELIM ;
if ( c = = ' . ' )
return IN6PTON_DOT ;
if ( c > = ' 0 ' & & c < = ' 9 ' )
return ( IN6PTON_DIGIT | ( c - ' 0 ' ) ) ;
return IN6PTON_UNKNOWN ;
}
static inline int xdigit2bin ( char c , char delim )
{
if ( c = = delim | | c = = ' \0 ' )
return IN6PTON_DELIM ;
if ( c = = ' : ' )
return IN6PTON_COLON_MASK ;
if ( c = = ' . ' )
return IN6PTON_DOT ;
if ( c > = ' 0 ' & & c < = ' 9 ' )
return ( IN6PTON_XDIGIT | IN6PTON_DIGIT | ( c - ' 0 ' ) ) ;
if ( c > = ' a ' & & c < = ' f ' )
return ( IN6PTON_XDIGIT | ( c - ' a ' + 10 ) ) ;
if ( c > = ' A ' & & c < = ' F ' )
return ( IN6PTON_XDIGIT | ( c - ' A ' + 10 ) ) ;
return IN6PTON_UNKNOWN ;
}
int in4_pton ( const char * src , int srclen ,
u8 * dst ,
char delim , const char * * end )
{
const char * s ;
u8 * d ;
u8 dbuf [ 4 ] ;
int ret = 0 ;
int i ;
int w = 0 ;
if ( srclen < 0 )
srclen = strlen ( src ) ;
s = src ;
d = dbuf ;
i = 0 ;
while ( 1 ) {
int c ;
c = xdigit2bin ( srclen > 0 ? * s : ' \0 ' , delim ) ;
if ( ! ( c & ( IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM ) ) ) {
goto out ;
}
if ( c & ( IN6PTON_DOT | IN6PTON_DELIM ) ) {
if ( w = = 0 )
goto out ;
* d + + = w & 0xff ;
w = 0 ;
i + + ;
if ( c & IN6PTON_DELIM ) {
if ( i ! = 4 )
goto out ;
break ;
}
goto cont ;
}
w = ( w * 10 ) + c ;
if ( ( w & 0xffff ) > 255 ) {
goto out ;
}
cont :
if ( i > = 4 )
goto out ;
s + + ;
srclen - - ;
}
ret = 1 ;
memcpy ( dst , dbuf , sizeof ( dbuf ) ) ;
out :
if ( end )
* end = s ;
return ret ;
}
EXPORT_SYMBOL ( in4_pton ) ;
int in6_pton ( const char * src , int srclen ,
u8 * dst ,
char delim , const char * * end )
{
const char * s , * tok = NULL ;
u8 * d , * dc = NULL ;
u8 dbuf [ 16 ] ;
int ret = 0 ;
int i ;
int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL ;
int w = 0 ;
memset ( dbuf , 0 , sizeof ( dbuf ) ) ;
s = src ;
d = dbuf ;
if ( srclen < 0 )
srclen = strlen ( src ) ;
while ( 1 ) {
int c ;
c = xdigit2bin ( srclen > 0 ? * s : ' \0 ' , delim ) ;
if ( ! ( c & state ) )
goto out ;
if ( c & ( IN6PTON_DELIM | IN6PTON_COLON_MASK ) ) {
/* process one 16-bit word */
if ( ! ( state & IN6PTON_NULL ) ) {
* d + + = ( w > > 8 ) & 0xff ;
* d + + = w & 0xff ;
}
w = 0 ;
if ( c & IN6PTON_DELIM ) {
/* We've processed last word */
break ;
}
/*
* COLON_1 = > XDIGIT
* COLON_2 = > XDIGIT | DELIM
* COLON_1_2 = > COLON_2
*/
switch ( state & IN6PTON_COLON_MASK ) {
case IN6PTON_COLON_2 :
dc = d ;
state = IN6PTON_XDIGIT | IN6PTON_DELIM ;
if ( dc - dbuf > = sizeof ( dbuf ) )
state | = IN6PTON_NULL ;
break ;
case IN6PTON_COLON_1 | IN6PTON_COLON_1_2 :
state = IN6PTON_XDIGIT | IN6PTON_COLON_2 ;
break ;
case IN6PTON_COLON_1 :
state = IN6PTON_XDIGIT ;
break ;
case IN6PTON_COLON_1_2 :
state = IN6PTON_COLON_2 ;
break ;
default :
state = 0 ;
}
tok = s + 1 ;
goto cont ;
}
if ( c & IN6PTON_DOT ) {
ret = in4_pton ( tok ? tok : s , srclen + ( int ) ( s - tok ) , d , delim , & s ) ;
if ( ret > 0 ) {
d + = 4 ;
break ;
}
goto out ;
}
w = ( w < < 4 ) | ( 0xff & c ) ;
state = IN6PTON_COLON_1 | IN6PTON_DELIM ;
if ( ! ( w & 0xf000 ) ) {
state | = IN6PTON_XDIGIT ;
}
if ( ! dc & & d + 2 < dbuf + sizeof ( dbuf ) ) {
state | = IN6PTON_COLON_1_2 ;
state & = ~ IN6PTON_DELIM ;
}
if ( d + 2 > = dbuf + sizeof ( dbuf ) ) {
state & = ~ ( IN6PTON_COLON_1 | IN6PTON_COLON_1_2 ) ;
}
cont :
if ( ( dc & & d + 4 < dbuf + sizeof ( dbuf ) ) | |
d + 4 = = dbuf + sizeof ( dbuf ) ) {
state | = IN6PTON_DOT ;
}
if ( d > = dbuf + sizeof ( dbuf ) ) {
state & = ~ ( IN6PTON_XDIGIT | IN6PTON_COLON_MASK ) ;
}
s + + ;
srclen - - ;
}
i = 15 ; d - - ;
if ( dc ) {
while ( d > = dc )
dst [ i - - ] = * d - - ;
while ( i > = dc - dbuf )
dst [ i - - ] = 0 ;
while ( i > = 0 )
dst [ i - - ] = * d - - ;
} else
memcpy ( dst , dbuf , sizeof ( dbuf ) ) ;
ret = 1 ;
out :
if ( end )
* end = s ;
return ret ;
}
EXPORT_SYMBOL ( in6_pton ) ;