File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / ip.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:39:23 2021 UTC (3 years, 3 months ago) by misho
Branches: mpd, MAIN
CVS tags: v5_9p16, v5_9, HEAD
mpd 5.9


/*
 * ip.c
 *
 * Written by Alexander Motin <mav@FreeBSD.org>
 */

#include "ppp.h"
#include "ip.h"
#include "util.h"

/*
 * IpShowRoutes()
 *
 * Show routing tables
 */

int
IpShowRoutes(Context ctx, int ac, const char *const av[], const void *arg)
{
  FILE	*fp;
  char	buf[256];
  char	*c;

  (void)ac;
  (void)av;
  (void)arg;

  if ((fp = popen(PATH_NETSTAT " -nr -f inet", "r")) == NULL)
  {
    Perror("popen");
    return(CMD_ERR_OTHER);
  }
  while (fgets(buf,sizeof(buf)-1,fp)) {
    if ((c=strrchr(buf,'\n')))
	*c='\r';
    Printf("%s\n",buf);
  }
  pclose(fp);
  return(0);
}

/*
 * IpAddrInRange()
 *
 * Is the IP address within the range?
 */

int
IpAddrInRange(struct u_range *range, struct u_addr *addr)
{
  u_int32_t mask;
  u_int8_t bmask;
  int	i;

  if (u_rangeempty(range))
    return(1);	// For compatibility we think that empty range includes any ip.

  if (range->addr.family!=addr->family)
    return(0);

  switch (range->addr.family) {
    case AF_INET:
	    mask = range->width ? htonl(~0 << (32 - range->width)) : 0;
	    return((addr->u.ip4.s_addr & mask) == (range->addr.u.ip4.s_addr & mask));
	break;
    case AF_INET6:
	    for (i=0; i<((range->width+7)/8); i++) {
		if ((i*8+7)<range->width) {
		    if (addr->u.ip6.s6_addr[i]!=range->addr.u.ip6.s6_addr[i])
			return 0;
		} else {
		    bmask=(range->width==i*8)? 0 : (~0 << (8 - range->width));
		    if ((addr->u.ip6.s6_addr[i] & bmask) != (range->addr.u.ip6.s6_addr[i] & bmask))
			return 0;
		}
	    }
	    return 1;
	break;
  }
  return 0;
}

/*
 * ParseAddr()
 *
 * Parse an IP address & mask of the form X.Y.Z.W/M.
 * If no slash, then M is assumed to be 32.
 * Returns TRUE if successful
 */

int
ParseAddr(const char *s, struct u_addr *addr, u_char allow)
{
  if (strchr(s, '/'))
    return (FALSE);

/* Get IP address part; if that fails, try looking up hostname */

  if ((allow&ALLOW_IPV4) && inet_pton(AF_INET, s, &addr->u.ip4))
  {
    addr->family=AF_INET;
  } 
  else if ((allow&ALLOW_IPV6) && inet_pton(AF_INET6, s, &addr->u.ip6))
  {
    addr->family=AF_INET6;
  } 
  else if ((allow&ALLOW_IPV4) && !GetAnyIpAddress(addr, s)) 
  {
    addr->family=AF_INET;
  }
  else if (allow&ALLOW_IPV4)
  {
  	struct hostent	*hptr;
	int		k;

	if ((hptr = gethostbyname(s)) == NULL) {
    	    return (FALSE);
	}
	for (k = 0; hptr->h_addr_list[k]; k++);
	if (k == 0)
	    k = 1;
	memcpy(&addr->u.ip4, hptr->h_addr_list[random() % k], sizeof(addr->u.ip4));
	addr->family=AF_INET;
  } 
  else 
  {
    return (FALSE);
  }

  return(TRUE);
}

/*
 * ParseRange()
 *
 * Parse an IP address & mask of the form X.Y.Z.W/M.
 * If no slash, then M is assumed to be 32.
 * Returns TRUE if successful
 */

int
ParseRange(const char *s, struct u_range *range, u_char allow)
{
  int			n_bits;
  char			*widp, buf[64];

  strlcpy(buf, s, sizeof(buf));
  if ((widp = strchr(buf, '/')) != NULL)
    *widp++ = '\0';
  else
    widp = buf + strlen(buf);

/* Get IP address part; if that fails, try looking up hostname */

  if (!ParseAddr(buf, &range->addr, allow)) 
    return(FALSE);

/* Get mask width */

  if (*widp)
  {
    if ((n_bits = atoi(widp)) < 0 || (range->addr.family==AF_INET && n_bits > 32) || (range->addr.family==AF_INET6 && n_bits > 128))
    {
      return(FALSE);
    }
  }
  else if (range->addr.family==AF_INET) {
    n_bits = 32;
  } else {
    n_bits = 128;
  }
  range->width = n_bits;

  return(TRUE);
}

/*
 * ParseAddrPort()
 *
 * Parse an IP address & port of the form X.Y.Z.W P.
 * Returns pointer to sockaddr_in. Not thread safe!
 */

struct sockaddr_storage *
ParseAddrPort(int ac, const char *const av[], u_char allow)
{
  static struct sockaddr_storage ss;
  struct u_addr addr;
  int		port = 0;

  if (ac < 1 || ac > 2)
    return (NULL);

  if (!ParseAddr(av[0], &addr, allow))
    return(NULL);

  if (ac > 1) {
    if ((port = atoi(av[1])) < 1 || port > 65535) {
      Log(LG_ERR, ("Bad port \"%s\"", av[1]));
      return (NULL);
    }
  }

  memset(&ss, 0, sizeof(ss));
  ss.ss_family = addr.family;
  switch (addr.family) {
    case AF_INET:
	ss.ss_len = sizeof(struct sockaddr_in);
	((struct sockaddr_in*)&ss)->sin_addr = addr.u.ip4;
	((struct sockaddr_in*)&ss)->sin_port = htons(port);
	break;
    case AF_INET6:
	ss.ss_len = sizeof(struct sockaddr_in6);
	((struct sockaddr_in6*)&ss)->sin6_addr = addr.u.ip6;
	((struct sockaddr_in6*)&ss)->sin6_port = htons(port);
	break;
    default:
	ss.ss_len = sizeof(struct sockaddr_storage);
  }

  return (&ss);
}

sa_family_t	u_addrfamily(struct u_addr *addr)
{
    return addr->family;
}

sa_family_t	u_rangefamily(struct u_range *range)
{
    return range->addr.family;
}

char   *u_addrtoa(struct u_addr *addr, char *dst, size_t size)
{
    dst[0]=0;
    
    if (addr->family==AF_INET) {
	inet_ntop(addr->family, &addr->u.ip4, dst, size);
    } 
    else if (addr->family==AF_INET6) 
    {
        inet_ntop(addr->family, &addr->u.ip6, dst, size);
    }
    else if (addr->family==AF_UNSPEC) 
    {
        snprintf(dst,size,"UNSPEC");
    }

    return dst;
}

char   *u_rangetoa(struct u_range *range, char *dst, size_t size)
{
    if (!u_addrtoa(&range->addr, dst, size))
	return NULL;

    if (range->addr.family!=AF_UNSPEC)
	snprintf(dst+strlen(dst), size-strlen(dst), "/%d", range->width);

    return dst;
}

void u_addrcopy(const struct u_addr *src, struct u_addr *dst)
{
    memcpy(dst,src,sizeof(struct u_addr));
}

void  u_rangecopy(const struct u_range *src, struct u_range *dst)
{
    memcpy(dst,src,sizeof(struct u_range));
}

void u_addrclear(struct u_addr *addr)
{
    memset(addr,0,sizeof(struct u_addr));
}

void u_rangeclear(struct u_range *range)
{
    memset(range,0,sizeof(struct u_range));
}

void in_addrtou_addr(const struct in_addr *src, struct u_addr *dst)
{
    u_addrclear(dst);
    dst->family=AF_INET;
    dst->u.ip4=*src;
}

void in6_addrtou_addr(const struct in6_addr *src, struct u_addr *dst)
{
    u_addrclear(dst);
    dst->family=AF_INET6;
    dst->u.ip6=*src;
}

void u_addrtoin_addr(const struct u_addr *src, struct in_addr *dst)
{
    *dst=src->u.ip4;
}

void u_addrtoin6_addr(const struct u_addr *src, struct in6_addr *dst)
{
    *dst=src->u.ip6;
}

void in_addrtou_range(const struct in_addr *src, u_char width, struct u_range *dst)
{
    u_rangeclear(dst);
    in_addrtou_addr(src, &dst->addr);
    dst->width = width;
}

void in6_addrtou_range(const struct in6_addr *src, u_char width, struct u_range *dst)
{
    u_rangeclear(dst);
    in6_addrtou_addr(src, &dst->addr);
    dst->width = width;
}

int u_addrempty(struct u_addr *addr)
{
    int i;
    switch (addr->family) {
	case AF_INET:
		return (addr->u.ip4.s_addr==0); 
	    break;
	case AF_INET6:
		for (i=0;i<16;i++) {
		    if (addr->u.ip6.s6_addr[i]!=0)
			return 0;
		}
		return 1;
	    break;
    }
    return 1;
}

int u_rangeempty(struct u_range *range)
{
    return u_addrempty(&range->addr);
}

int u_rangehost(struct u_range *range)
{
    switch (range->addr.family) {
	case AF_INET:
		return (range->width==32); 
	    break;
	case AF_INET6:
		return (range->width==128); 
	    break;
    }
    return 0;
}

static struct in6_addr
bits2mask6(int bits)
{
  struct in6_addr result;
  u_int32_t bit = 0x80;
  u_char *c = result.s6_addr;

  memset(&result, '\0', sizeof(result));

  while (bits) {
    if (bit == 0) {
      bit = 0x80;
      c++;
    }
    *c |= bit;
    bit >>= 1;
    bits--;
  }

  return result;
}

void u_rangetosockaddrs(struct u_range *range, struct sockaddr_storage *dst, struct sockaddr_storage *msk)
{
    memset(dst,0,sizeof(struct sockaddr_storage));
    memset(msk,0,sizeof(struct sockaddr_storage));
    dst->ss_family = msk->ss_family = range->addr.family;
    switch (range->addr.family) {
	case AF_INET:
		((struct sockaddr_in*)dst)->sin_len=sizeof(struct sockaddr_in);
		((struct sockaddr_in*)dst)->sin_addr=range->addr.u.ip4;
		((struct sockaddr_in*)msk)->sin_len=sizeof(struct sockaddr_in);
		((struct sockaddr_in*)msk)->sin_addr.s_addr=htonl(0xFFFFFFFFLL<<(32 - range->width));
	    break;
	case AF_INET6:
		((struct sockaddr_in6*)dst)->sin6_len=sizeof(struct sockaddr_in6);
		((struct sockaddr_in6*)dst)->sin6_addr=range->addr.u.ip6;
		((struct sockaddr_in6*)msk)->sin6_len=sizeof(struct sockaddr_in6);
		((struct sockaddr_in6*)msk)->sin6_addr=bits2mask6(range->width);
	    break;
	default:
		dst->ss_len=sizeof(struct sockaddr_storage);
	    break;
    }
}

void u_addrtosockaddr(struct u_addr *addr, in_port_t port, struct sockaddr_storage *dst)
{
    memset(dst,0,sizeof(struct sockaddr_storage));
    dst->ss_family=addr->family;
    switch (addr->family) {
	case AF_INET:
		((struct sockaddr_in*)dst)->sin_len=sizeof(struct sockaddr_in);
		((struct sockaddr_in*)dst)->sin_addr=addr->u.ip4;
		((struct sockaddr_in*)dst)->sin_port=htons(port);
	    break;
	case AF_INET6:
		((struct sockaddr_in6*)dst)->sin6_len=sizeof(struct sockaddr_in6);
		((struct sockaddr_in6*)dst)->sin6_addr=addr->u.ip6;
		((struct sockaddr_in6*)dst)->sin6_port=htons(port);
	    break;
	default:
		dst->ss_len=sizeof(struct sockaddr_storage);
	    break;
    }
}

void sockaddrtou_addr(struct sockaddr_storage *src, struct u_addr *addr, in_port_t *port)
{
    addr->family=src->ss_family;
    switch (addr->family) {
	case AF_INET:
		addr->u.ip4=((struct sockaddr_in*)src)->sin_addr;
		*port=ntohs(((struct sockaddr_in*)src)->sin_port);
	    break;
	case AF_INET6:
		addr->u.ip6=((struct sockaddr_in6*)src)->sin6_addr;
		*port=ntohs(((struct sockaddr_in6*)src)->sin6_port);
	    break;
	default:
	    memset(addr,0,sizeof(struct u_addr));
	    *port=0;
	    break;
    }
}

int u_addrcompare(const struct u_addr *addr1, const struct u_addr *addr2)
{
  int	i;

  if (addr1->family<addr2->family)
    return(-1);
  else if (addr1->family>addr2->family)
    return(1);
  
  switch (addr1->family) {
    case AF_INET:
	    if (addr1->u.ip4.s_addr < addr2->u.ip4.s_addr)
		return (-1);
	    else if (addr1->u.ip4.s_addr == addr2->u.ip4.s_addr)
		return (0);
	    else 
		return (1);
	break;
    case AF_INET6:
	    for (i=0; i<16; i++) {
		if (addr1->u.ip6.s6_addr[i] < addr2->u.ip6.s6_addr[i])
		    return (-1);
		else if (addr1->u.ip6.s6_addr[i] > addr2->u.ip6.s6_addr[i])
		    return (1);
	    }
	    return 0;
	break;
  }
  return 0;
}

int u_rangecompare(const struct u_range *range1, const struct u_range *range2)
{
  if (range1->width<range2->width)
    return(-1);
  else if (range1->width>range2->width)
    return(1);

  return u_addrcompare(&range1->addr,&range2->addr);
}

uint32_t
u_addrtoid(const struct u_addr *addr)
{
    uint32_t id;

    if (addr->family==AF_INET) {
	id = ntohl(addr->u.ip4.s_addr);
    } else if (addr->family==AF_INET6) {
	const uint32_t *a32 = (const uint32_t *)(const void *)(&addr->u.ip6.s6_addr[0]);
        id = a32[0] + a32[1] + a32[2] + a32[3];
    } else {
	id = 0;
    }

    return (id);
}

u_char
in_addrtowidth(struct in_addr *mask)
{
	u_char width = 0;
	uint32_t m = ntohl(mask->s_addr);

	while ((m & 0x80000000) != 0) {
		m <<= 1;
		width++;
	}
	return (width);
}

struct in_addr *
widthtoin_addr(u_char width, struct in_addr *mask)
{
	mask->s_addr = htonl(0xFFFFFFFF << (32 - width));
	return (mask);
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>