Skip to content

Commit

Permalink
Parse ipv4prefix correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
alandekok committed Oct 17, 2015
1 parent d79735f commit 9ca5c3b
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 18 deletions.
98 changes: 80 additions & 18 deletions src/lib/inet.c
Expand Up @@ -248,22 +248,82 @@ char const *fr_inet_ntoh(fr_ipaddr_t const *src, char *out, size_t outlen)
return out;
}


/*
* Parse decimal digits until we run out of decimal digits.
*/
static int ip_octet_from_str(char const *str, uint32_t *poctet)
{
uint32_t octet;
char const *p = str;

if ((*p < '0') || (*p > '9')) {
return -1;
}

octet = 0;

while ((*p >= '0') && (*p <= '9')) {
octet *= 10;
octet += *p - '0';
p++;

if (octet > 255) return -1;
}


*poctet = octet;
return p - str;
}

static int ip_prefix_from_str(char const *str, uint32_t *paddr)
{
int shift, length;
uint32_t octet;
uint32_t addr;
char const *p = str;

addr = 0;

for (shift = 24; shift >= 0; shift -= 8) {
length = ip_octet_from_str(p, &octet);
if (length <= 0) return -1;

addr |= octet << shift;
p += length;

/*
* EOS or / means we're done.
*/
if (!*p || (*p == '/')) break;

/*
* We require dots between octets.
*/
if (*p != '.') return -1;
p++;
}

*paddr = htonl(addr);
return p - str;
}

/** Parse an IPv4 address or IPv4 prefix in presentation format (and others)
*
* @param out Where to write the ip address value.
* @param value to parse, may be dotted quad [+ prefix], or integer, or octal number, or '*' (INADDR_ANY).
* @param inlen Length of value, if value is \0 terminated inlen may be -1.
* @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
* @param fallback to IPv6 resolution if no A records can be found.
* @param mask If true, set address bits to zero.
* @param mask_bits If true, set address bits to zero.
* @return
* - 0 if ip address was parsed successfully
* - -1 on failure.
*/
int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback, bool mask)
int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback, bool mask_bits)
{
char *p;
unsigned int prefix;
unsigned int mask;
char *eptr;

/* Dotted quad + / + [0-9]{1,2} */
Expand All @@ -283,6 +343,7 @@ int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resol
}

p = strchr(value, '/');

/*
* 192.0.2.2 is parsed as if it was /32
*/
Expand All @@ -295,6 +356,7 @@ int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resol
*/
if ((value[0] == '*') && (value[1] == '\0')) {
out->ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);

/*
* Convert things which are obviously integers to IP addresses
*
Expand All @@ -303,9 +365,10 @@ int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resol
*/
} else if (is_integer(value) || ((value[0] == '0') && (value[1] == 'x'))) {
out->ipaddr.ip4addr.s_addr = htonl(strtoul(value, NULL, 0));

} else if (!resolve) {
if (inet_pton(AF_INET, value, &out->ipaddr.ip4addr.s_addr) <= 0) {
fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
fr_strerror_printf("Failed to parse IPv4 addreess string \"%s\"", value);
return -1;
}
} else if (fr_inet_hton(out, AF_INET, value, fallback) < 0) return -1;
Expand All @@ -314,11 +377,11 @@ int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resol
}

/*
* Otherwise parse the prefix
* Otherwise parse the prefix
*/
if ((size_t)(p - value) >= INET_ADDRSTRLEN) {
fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
return -1;
fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
return -1;
}

/*
Expand All @@ -327,29 +390,28 @@ int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resol
if (inlen < 0) memcpy(buffer, value, p - value);
buffer[p - value] = '\0';

if (!resolve) {
if (inet_pton(AF_INET, buffer, &out->ipaddr.ip4addr.s_addr) <= 0) {
fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
return -1;
}
} else if (fr_inet_hton(out, AF_INET, buffer, fallback) < 0) return -1;
if (ip_prefix_from_str(buffer, &out->ipaddr.ip4addr.s_addr) <= 0) {
fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
return -1;
}

prefix = strtoul(p + 1, &eptr, 10);
if (prefix > 32) {
mask = strtoul(p + 1, &eptr, 10);
if (mask > 32) {
fr_strerror_printf("Invalid IPv4 mask length \"%s\". Should be between 0-32", p);
return -1;
}

if (eptr[0] != '\0') {
fr_strerror_printf("Failed to parse IPv4 address string \"%s\", "
"got garbage after mask length \"%s\"", value, eptr);
return -1;
}

if (mask && (prefix < 32)) {
out->ipaddr.ip4addr = fr_inaddr_mask(&out->ipaddr.ip4addr, prefix);
if (mask_bits && (mask < 32)) {
out->ipaddr.ip4addr = fr_inaddr_mask(&out->ipaddr.ip4addr, mask);
}

out->prefix = (uint8_t) prefix;
out->prefix = (uint8_t) mask;
out->af = AF_INET;

return 0;
Expand Down
43 changes: 43 additions & 0 deletions src/tests/unit/rfc.txt
Expand Up @@ -120,6 +120,9 @@ data Framed-IP-Address = 0.0.0.0
attribute Framed-IP-Address = 127
data Framed-IP-Address = 0.0.0.127

attribute Framed-IP-Address = 127.0
data Framed-IP-Address = 127.0.0.0

attribute Framed-IPv6-Prefix = ::1
data Framed-IPv6-Prefix = ::1/128

Expand All @@ -135,6 +138,46 @@ data Framed-IPv6-Prefix = 11:22:33:44:55:66:77:88/128
attribute Framed-IPv6-Prefix = *
data Framed-IPv6-Prefix = ::/128

attribute PMIP6-Home-IPv4-HoA = 127/8
data PMIP6-Home-IPv4-HoA = 127.0.0.0/8

attribute PMIP6-Home-IPv4-HoA = 127/8
data PMIP6-Home-IPv4-HoA = 127.0.0.0/8

#
# Octets outside of the mask are OK, but
# are mashed to zero.
#
attribute PMIP6-Home-IPv4-HoA = 127.63/8
data PMIP6-Home-IPv4-HoA = 127.0.0.0/8

#
# Unless you give a good mask.
#
attribute PMIP6-Home-IPv4-HoA = 127.63/16
data PMIP6-Home-IPv4-HoA = 127.63.0.0/16

attribute PMIP6-Home-IPv4-HoA = 127.999/16
data Failed to parse IPv4 address string "127.999/16"

attribute PMIP6-Home-IPv4-HoA = 127.bob/16
data Failed to parse IPv4 address string "127.bob/16"

attribute PMIP6-Home-IPv4-HoA = 127.63/15
data PMIP6-Home-IPv4-HoA = 127.62.0.0/15

attribute PMIP6-Home-IPv4-HoA = 127.63.1/24
data PMIP6-Home-IPv4-HoA = 127.63.1.0/24

attribute PMIP6-Home-IPv4-HoA = 127.63.1.6
data PMIP6-Home-IPv4-HoA = 127.63.1.6/32

attribute PMIP6-Home-IPv4-HoA = 256/8
data Failed to parse IPv4 address string "256/8"

attribute PMIP6-Home-IPv4-HoA = bob/8
data Failed to parse IPv4 address string "bob/8"

$INCLUDE tunnel.txt
$INCLUDE errors.txt
$INCLUDE extended.txt
Expand Down

0 comments on commit 9ca5c3b

Please sign in to comment.