2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* Generic address resultion entity
*
* Authors :
* net_random Alan Cox
2006-09-28 09:55:39 +04:00
* net_ratelimit Andi 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 >
*/
# include <linux/module.h>
# include <linux/jiffies.h>
# include <linux/kernel.h>
2013-03-27 09:54:13 +04:00
# include <linux/ctype.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/percpu.h>
# include <linux/init.h>
2009-09-22 18:18:09 +04:00
# include <linux/ratelimit.h>
2017-02-05 22:47:22 +03:00
# include <linux/socket.h>
2009-09-22 18:18:09 +04:00
2007-08-08 05:02:43 +04:00
# include <net/sock.h>
2011-05-27 21:41:33 +04:00
# include <net/net_ratelimit.h>
2017-02-05 22:47:22 +03:00
# include <net/ipv6.h>
2005-04-17 02:20:36 +04:00
2005-07-28 02:24:42 +04:00
# include <asm/byteorder.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
2008-07-25 12:45:58 +04:00
DEFINE_RATELIMIT_STATE ( net_ratelimit_state , 5 * HZ , 10 ) ;
2007-02-09 17:24:36 +03:00
/*
2005-04-17 02:20:36 +04:00
* All net warning printk ( ) s should be guarded by this function .
2007-02-09 17:24:36 +03:00
*/
2005-04-17 02:20:36 +04:00
int net_ratelimit ( void )
{
2008-07-25 12:45:58 +04:00
return __ratelimit ( & net_ratelimit_state ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( net_ratelimit ) ;
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
{
2017-03-24 00:58:26 +03:00
unsigned int l ;
2005-07-28 02:24:42 +04:00
unsigned int val ;
int i ;
l = 0 ;
2012-04-08 10:07:42 +04:00
for ( i = 0 ; i < 4 ; i + + ) {
2005-07-28 02:24:42 +04:00
l < < = 8 ;
2012-04-08 10:07:42 +04:00
if ( * str ! = ' \0 ' ) {
2005-07-28 02:24:42 +04:00
val = 0 ;
2012-04-08 10:07:42 +04: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 + + ;
}
}
2010-09-23 00:43:57 +04:00
return htonl ( l ) ;
2005-07-28 02:24:42 +04:00
}
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
2006-12-03 09:04:04 +03:00
static inline int xdigit2bin ( char c , int delim )
2006-06-25 18:54:55 +04:00
{
2010-09-21 00:40:26 +04:00
int val ;
2006-06-25 18:54:55 +04:00
if ( c = = delim | | c = = ' \0 ' )
return IN6PTON_DELIM ;
if ( c = = ' : ' )
return IN6PTON_COLON_MASK ;
if ( c = = ' . ' )
return IN6PTON_DOT ;
2010-09-21 00:40:26 +04:00
val = hex_to_bin ( c ) ;
if ( val > = 0 )
return val | IN6PTON_XDIGIT | ( val < 10 ? IN6PTON_DIGIT : 0 ) ;
2006-12-03 09:04:04 +03:00
if ( delim = = - 1 )
return IN6PTON_DELIM ;
2006-06-25 18:54:55 +04:00
return IN6PTON_UNKNOWN ;
}
2012-10-12 01:06:17 +04:00
/**
* in4_pton - convert an IPv4 address from literal to binary representation
* @ src : the start of the IPv4 address string
* @ srclen : the length of the string , - 1 means strlen ( src )
* @ dst : the binary ( u8 [ 4 ] array ) representation of the IPv4 address
* @ delim : the delimiter of the IPv4 address in @ src , - 1 means no delimiter
* @ end : A pointer to the end of the parsed string will be placed here
*
* Return one on success , return zero when any error occurs
* and @ end will point to the end of the parsed string .
*
*/
2006-06-25 18:54:55 +04:00
int in4_pton ( const char * src , int srclen ,
u8 * dst ,
2006-12-03 09:04:04 +03:00
int delim , const char * * end )
2006-06-25 18:54:55 +04:00
{
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 ;
2016-06-27 13:12:46 +03:00
while ( 1 ) {
2006-06-25 18:54:55 +04:00
int c ;
c = xdigit2bin ( srclen > 0 ? * s : ' \0 ' , delim ) ;
2007-05-29 23:59:54 +04:00
if ( ! ( c & ( IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK ) ) ) {
2006-06-25 18:54:55 +04:00
goto out ;
}
2007-05-29 23:59:54 +04:00
if ( c & ( IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK ) ) {
2006-06-25 18:54:55 +04:00
if ( w = = 0 )
goto out ;
* d + + = w & 0xff ;
w = 0 ;
i + + ;
2007-05-29 23:59:54 +04:00
if ( c & ( IN6PTON_DELIM | IN6PTON_COLON_MASK ) ) {
2006-06-25 18:54:55 +04:00
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 ) ;
2012-10-12 01:06:16 +04:00
/**
* in6_pton - convert an IPv6 address from literal to binary representation
* @ src : the start of the IPv6 address string
* @ srclen : the length of the string , - 1 means strlen ( src )
* @ dst : the binary ( u8 [ 16 ] array ) representation of the IPv6 address
* @ delim : the delimiter of the IPv6 address in @ src , - 1 means no delimiter
* @ end : A pointer to the end of the parsed string will be placed here
*
* Return one on success , return zero when any error occurs
* and @ end will point to the end of the parsed string .
*
*/
2006-06-25 18:54:55 +04:00
int in6_pton ( const char * src , int srclen ,
u8 * dst ,
2006-12-03 09:04:04 +03:00
int delim , const char * * end )
2006-06-25 18:54:55 +04:00
{
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 ) {
2016-06-27 13:12:46 +03:00
while ( d > = dc )
2006-06-25 18:54:55 +04:00
dst [ i - - ] = * d - - ;
2016-06-27 13:12:46 +03:00
while ( i > = dc - dbuf )
2006-06-25 18:54:55 +04:00
dst [ i - - ] = 0 ;
2016-06-27 13:12:46 +03:00
while ( i > = 0 )
2006-06-25 18:54:55 +04:00
dst [ i - - ] = * d - - ;
} else
memcpy ( dst , dbuf , sizeof ( dbuf ) ) ;
ret = 1 ;
out :
if ( end )
* end = s ;
return ret ;
}
EXPORT_SYMBOL ( in6_pton ) ;
2007-11-29 17:14:30 +03:00
2017-02-05 22:47:22 +03:00
static int inet4_pton ( const char * src , u16 port_num ,
struct sockaddr_storage * addr )
{
struct sockaddr_in * addr4 = ( struct sockaddr_in * ) addr ;
2022-10-29 04:46:04 +03:00
size_t srclen = strlen ( src ) ;
2017-02-05 22:47:22 +03:00
if ( srclen > INET_ADDRSTRLEN )
return - EINVAL ;
if ( in4_pton ( src , srclen , ( u8 * ) & addr4 - > sin_addr . s_addr ,
' \n ' , NULL ) = = 0 )
return - EINVAL ;
addr4 - > sin_family = AF_INET ;
addr4 - > sin_port = htons ( port_num ) ;
return 0 ;
}
static int inet6_pton ( struct net * net , const char * src , u16 port_num ,
struct sockaddr_storage * addr )
{
struct sockaddr_in6 * addr6 = ( struct sockaddr_in6 * ) addr ;
const char * scope_delim ;
2022-10-29 04:46:04 +03:00
size_t srclen = strlen ( src ) ;
2017-02-05 22:47:22 +03:00
if ( srclen > INET6_ADDRSTRLEN )
return - EINVAL ;
if ( in6_pton ( src , srclen , ( u8 * ) & addr6 - > sin6_addr . s6_addr ,
' % ' , & scope_delim ) = = 0 )
return - EINVAL ;
if ( ipv6_addr_type ( & addr6 - > sin6_addr ) & IPV6_ADDR_LINKLOCAL & &
src + srclen ! = scope_delim & & * scope_delim = = ' % ' ) {
struct net_device * dev ;
char scope_id [ 16 ] ;
2017-04-13 22:42:31 +03:00
size_t scope_len = min_t ( size_t , sizeof ( scope_id ) - 1 ,
2017-02-05 22:47:22 +03:00
src + srclen - scope_delim - 1 ) ;
memcpy ( scope_id , scope_delim + 1 , scope_len ) ;
scope_id [ scope_len ] = ' \0 ' ;
dev = dev_get_by_name ( net , scope_id ) ;
if ( dev ) {
addr6 - > sin6_scope_id = dev - > ifindex ;
dev_put ( dev ) ;
} else if ( kstrtouint ( scope_id , 0 , & addr6 - > sin6_scope_id ) ) {
return - EINVAL ;
}
}
addr6 - > sin6_family = AF_INET6 ;
addr6 - > sin6_port = htons ( port_num ) ;
return 0 ;
}
/**
* inet_pton_with_scope - convert an IPv4 / IPv6 and port to socket address
* @ net : net namespace ( used for scope handling )
* @ af : address family , AF_INET , AF_INET6 or AF_UNSPEC for either
* @ src : the start of the address string
* @ port : the start of the port string ( or NULL for none )
* @ addr : output socket address
*
* Return zero on success , return errno when any error occurs .
*/
int inet_pton_with_scope ( struct net * net , __kernel_sa_family_t af ,
const char * src , const char * port , struct sockaddr_storage * addr )
{
u16 port_num ;
int ret = - EINVAL ;
if ( port ) {
if ( kstrtou16 ( port , 0 , & port_num ) )
return - EINVAL ;
} else {
port_num = 0 ;
}
switch ( af ) {
case AF_INET :
ret = inet4_pton ( src , port_num , addr ) ;
break ;
case AF_INET6 :
ret = inet6_pton ( net , src , port_num , addr ) ;
break ;
case AF_UNSPEC :
ret = inet4_pton ( src , port_num , addr ) ;
if ( ret )
ret = inet6_pton ( net , src , port_num , addr ) ;
break ;
default :
pr_err ( " unexpected address family %d \n " , af ) ;
2018-08-04 14:41:41 +03:00
}
2017-02-05 22:47:22 +03:00
return ret ;
}
EXPORT_SYMBOL ( inet_pton_with_scope ) ;
2018-01-24 21:24:24 +03:00
bool inet_addr_is_any ( struct sockaddr * addr )
{
if ( addr - > sa_family = = AF_INET6 ) {
struct sockaddr_in6 * in6 = ( struct sockaddr_in6 * ) addr ;
const struct sockaddr_in6 in6_any =
{ . sin6_addr = IN6ADDR_ANY_INIT } ;
if ( ! memcmp ( in6 - > sin6_addr . s6_addr ,
in6_any . sin6_addr . s6_addr , 16 ) )
return true ;
} else if ( addr - > sa_family = = AF_INET ) {
struct sockaddr_in * in = ( struct sockaddr_in * ) addr ;
if ( in - > sin_addr . s_addr = = htonl ( INADDR_ANY ) )
return true ;
} else {
pr_warn ( " unexpected address family %u \n " , addr - > sa_family ) ;
}
return false ;
}
EXPORT_SYMBOL ( inet_addr_is_any ) ;
2007-11-29 17:14:30 +03:00
void inet_proto_csum_replace4 ( __sum16 * sum , struct sk_buff * skb ,
2015-08-17 23:42:25 +03:00
__be32 from , __be32 to , bool pseudohdr )
2007-11-29 17:14:30 +03:00
{
if ( skb - > ip_summed ! = CHECKSUM_PARTIAL ) {
2015-05-26 04:50:01 +03:00
csum_replace4 ( sum , from , to ) ;
2007-11-29 17:14:30 +03:00
if ( skb - > ip_summed = = CHECKSUM_COMPLETE & & pseudohdr )
2015-05-26 04:50:01 +03:00
skb - > csum = ~ csum_add ( csum_sub ( ~ ( skb - > csum ) ,
( __force __wsum ) from ) ,
( __force __wsum ) to ) ;
2007-11-29 17:14:30 +03:00
} else if ( pseudohdr )
2015-05-26 04:50:01 +03:00
* sum = ~ csum_fold ( csum_add ( csum_sub ( csum_unfold ( * sum ) ,
( __force __wsum ) from ) ,
( __force __wsum ) to ) ) ;
2007-11-29 17:14:30 +03:00
}
EXPORT_SYMBOL ( inet_proto_csum_replace4 ) ;
2011-05-08 03:00:07 +04:00
2020-01-23 23:33:28 +03:00
/**
* inet_proto_csum_replace16 - update layer 4 header checksum field
* @ sum : Layer 4 header checksum field
* @ skb : sk_buff for the packet
* @ from : old IPv6 address
* @ to : new IPv6 address
* @ pseudohdr : True if layer 4 header checksum includes pseudoheader
*
* Update layer 4 header as per the update in IPv6 src / dst address .
*
* There is no need to update skb - > csum in this function , because update in two
* fields a . ) IPv6 src / dst address and b . ) L4 header checksum cancels each other
* for skb - > csum calculation . Whereas inet_proto_csum_replace4 function needs to
* update skb - > csum , because update in 3 fields a . ) IPv4 src / dst address ,
* b . ) IPv4 Header checksum and c . ) L4 header checksum results in same diff as
* L4 Header checksum for skb - > csum calculation .
*/
2012-08-26 21:14:10 +04:00
void inet_proto_csum_replace16 ( __sum16 * sum , struct sk_buff * skb ,
const __be32 * from , const __be32 * to ,
2015-08-17 23:42:25 +03:00
bool pseudohdr )
2012-08-26 21:14:10 +04:00
{
__be32 diff [ ] = {
~ from [ 0 ] , ~ from [ 1 ] , ~ from [ 2 ] , ~ from [ 3 ] ,
to [ 0 ] , to [ 1 ] , to [ 2 ] , to [ 3 ] ,
} ;
if ( skb - > ip_summed ! = CHECKSUM_PARTIAL ) {
* sum = csum_fold ( csum_partial ( diff , sizeof ( diff ) ,
~ csum_unfold ( * sum ) ) ) ;
} else if ( pseudohdr )
* sum = ~ csum_fold ( csum_partial ( diff , sizeof ( diff ) ,
csum_unfold ( * sum ) ) ) ;
}
EXPORT_SYMBOL ( inet_proto_csum_replace16 ) ;
2013-10-19 23:48:55 +04:00
2015-08-17 23:42:26 +03:00
void inet_proto_csum_replace_by_diff ( __sum16 * sum , struct sk_buff * skb ,
__wsum diff , bool pseudohdr )
{
if ( skb - > ip_summed ! = CHECKSUM_PARTIAL ) {
2022-02-18 11:03:48 +03:00
csum_replace_by_diff ( sum , diff ) ;
2015-08-17 23:42:26 +03:00
if ( skb - > ip_summed = = CHECKSUM_COMPLETE & & pseudohdr )
2022-02-18 11:03:48 +03:00
skb - > csum = ~ csum_sub ( diff , skb - > csum ) ;
2015-08-17 23:42:26 +03:00
} else if ( pseudohdr ) {
* sum = ~ csum_fold ( csum_add ( diff , csum_unfold ( * sum ) ) ) ;
}
}
EXPORT_SYMBOL ( inet_proto_csum_replace_by_diff ) ;