Skip to content
Snippets Groups Projects
utils.c 5.78 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     *	Generic address resultion entity
     *
     *	Authors:
     *	net_random Alan Cox
    
     *	net_ratelimit Andi Kleen
    
     *	in{4,6}_pton YOSHIFUJI Hideaki, Copyright (C)2006 USAGI/WIDE Project
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     *
     *	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>
    
    #include <linux/inet.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <linux/mm.h>
    
    #include <linux/net.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <linux/string.h>
    #include <linux/types.h>
    #include <linux/random.h>
    #include <linux/percpu.h>
    #include <linux/init.h>
    
    
    #include <asm/byteorder.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <asm/system.h>
    #include <asm/uaccess.h>
    
    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_ratelimit);
    
    
    /*
     * 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.
     */
    
    
    __be32 in_aton(const char *str)
    
    {
    	unsigned long l;
    	unsigned int val;
    	int i;
    
    	l = 0;
    	for (i = 0; i < 4; i++)
    	{
    		l <<= 8;
    		if (*str != '\0')
    		{
    			val = 0;
    
    			while (*str != '\0' && *str != '.' && *str != '\n')
    
    			{
    				val *= 10;
    				val += *str - '0';
    				str++;
    			}
    			l |= val;
    			if (*str != '\0')
    				str++;
    		}
    	}
    	return(htonl(l));
    }
    
    EXPORT_SYMBOL(in_aton);
    
    
    #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, int 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, int 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));
    
    	if (delim == -1)
    		return IN6PTON_DELIM;
    
    	return IN6PTON_UNKNOWN;
    }
    
    int in4_pton(const char *src, int srclen,
    	     u8 *dst,
    
    	     int 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,
    
    	     int 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);