IPv6 link-local addresses and init data type
Hi all
I just ran into an issue that was originally reported way back in 2007 - /messages/by-id/0262B803-B664-4EBE-85B4-3C9A40EA6CDA@mobygames.com </messages/by-id/0262B803-B664-4EBE-85B4-3C9A40EA6CDA@mobygames.com>
Basically the inet data type cannot store or parse valid ipv6 address literals with a scope / zone id suffix. Apparently the combination of virtualised linux, ipv6 network and JVM that we are using has combined to report connections on localhost as coming from ‘::1%0’, which our app is unsuccessfully attempting to store in the db in an inet column. This is the first time that I have ever seen this, but perhaps it will get more common as ipv6-first usage increases.
Given that inet is a varlena struct with only known-length fields, it seems potentially possible to extend it to add an optional, variable length zone id on the end, with the result being backwards compatible with existing data.
Thoughts?
Cheers
Tom
Tom Dunstan <pgsql@tomd.cc> writes:
Basically the inet data type cannot store or parse valid ipv6 address literals with a scope / zone id suffix. Apparently the combination of virtualised linux, ipv6 network and JVM that we are using has combined to report connections on localhost as coming from ‘::1%0’, which our app is unsuccessfully attempting to store in the db in an inet column. This is the first time that I have ever seen this, but perhaps it will get more common as ipv6-first usage increases.
Given that inet is a varlena struct with only known-length fields, it seems potentially possible to extend it to add an optional, variable length zone id on the end, with the result being backwards compatible with existing data.
Thoughts?
The impression I have is that scopes are not very well standardized ---
eg, OS X reports things like "fe80::1%lo0" not just "%0". If we could
get around that problem it would be worth doing.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 31 May 2016, at 2:07 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The impression I have is that scopes are not very well standardized ---
eg, OS X reports things like "fe80::1%lo0" not just "%0". If we could
get around that problem it would be worth doing.
Yeah, we’d have to just store it as a string I think. That’s why I was happy to see that inet was already a varlena but only with known-length content.
Cheers
Tom
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, May 31, 2016 at 9:35 AM, Tom Dunstan <pgsql@tomd.cc> wrote:
On 31 May 2016, at 2:07 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The impression I have is that scopes are not very well standardized ---
eg, OS X reports things like "fe80::1%lo0" not just "%0". If we could
get around that problem it would be worth doing.
The following is the content of IPV6 representation from RFC 4007
The following addresses
fe80::1234 (on the 1st link of the node)
ff02::5678 (on the 5th link of the node)
ff08::9abc (on the 10th organization of the node)
would be represented as follows:
fe80::1234%1
ff02::5678%5
ff08::9abc%10
(Here we assume a natural translation from a zone index to the
<zone_id> part, where the Nth zone of any scope is translated into
"N".)
If we use interface names as <zone_id>, those addresses could also be
represented as follows:
fe80::1234%ne0
ff02::5678%pvc1.3
ff08::9abc%interface10
The digit and string both are considered as proper format.
Yeah, we’d have to just store it as a string I think. That’s why I was happy to see that inet was already a varlena but only with known-length content.
The % delimiter character is not only used at the end of the IPV6 address,
from the RFC document, it is possible as follows also.
fe80::%2/64
we need to handle both the scenarios, it may not be a straight
forward to store the zone id data.
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 05/31/2016 02:37 AM, Haribabu Kommi wrote:
The following is the content of IPV6 representation from RFC 4007
The following addresses
fe80::1234 (on the 1st link of the node)
ff02::5678 (on the 5th link of the node)
ff08::9abc (on the 10th organization of the node)would be represented as follows:
fe80::1234%1
ff02::5678%5
ff08::9abc%10(Here we assume a natural translation from a zone index to the
<zone_id> part, where the Nth zone of any scope is translated into
"N".)If we use interface names as <zone_id>, those addresses could also be
represented as follows:fe80::1234%ne0
ff02::5678%pvc1.3
ff08::9abc%interface10The digit and string both are considered as proper format.
Since we cannot tell if %1 is the same interface as %ne0, isn't the best
thing we can do to just store them as raw strings and treat them as
different addresses from PostgreSQL's point of view? I do not see any
problem with doing so.
The % delimiter character is not only used at the end of the IPV6 address,
from the RFC document, it is possible as follows also.fe80::%2/64
we need to handle both the scenarios, it may not be a straight
forward to store the zone id data.
This case needs to be handled by the parser for at least the cidr type,
but I do not think it would make parsing any trickier.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andreas Karlsson <andreas@proxel.se> writes:
On 05/31/2016 02:37 AM, Haribabu Kommi wrote:
The % delimiter character is not only used at the end of the IPV6 address,
from the RFC document, it is possible as follows also.fe80::%2/64
we need to handle both the scenarios, it may not be a straight
forward to store the zone id data.
This case needs to be handled by the parser for at least the cidr type,
but I do not think it would make parsing any trickier.
Unless there's a semantic difference between fe80::1%2/64 and
fe80::1/64%2, this doesn't seem like a big deal to me.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 05/31/2016 04:06 AM, Tom Lane wrote:
Andreas Karlsson <andreas@proxel.se> writes:
On 05/31/2016 02:37 AM, Haribabu Kommi wrote:
The % delimiter character is not only used at the end of the IPV6 address,
from the RFC document, it is possible as follows also.fe80::%2/64
we need to handle both the scenarios, it may not be a straight
forward to store the zone id data.This case needs to be handled by the parser for at least the cidr type,
but I do not think it would make parsing any trickier.Unless there's a semantic difference between fe80::1%2/64 and
fe80::1/64%2, this doesn't seem like a big deal to me.
As far as I can till only fe80::1%2/64 is valid, but I am not 100% sure.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 31.05.2016 12:40, Andreas Karlsson wrote:
On 05/31/2016 04:06 AM, Tom Lane wrote:
Unless there's a semantic difference between fe80::1%2/64 and
fe80::1/64%2, this doesn't seem like a big deal to me.As far as I can till only fe80::1%2/64 is valid, but I am not 100% sure.
According to RFC 4007, Section 11.7 states: "In this combination, it is
important to place the zone index portion before the prefix length when
we consider parsing the format by a name-to-address library function
[11]: . That is, we can first separate the address with the zone index from the prefix length, and just pass the former to the library function."
from the prefix length, and just pass the former to the library function."
However, in the sense of being liberal in what you accept, 'fe80::/64%2'
should probably work as well.
Given that a zone_id is a) highly system dependent and b) only ever
meaningful for non-global addresses, I'm wondering what the use case for
storing them is.
I'm even wondering if 'fe80::1%1'::inet = 'fe80::1%2'::inet shouldn't
simply yield true. After all, it's the same (non-global) address.
Kind Regards
Markus Wanner
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Markus Wanner <markus@bluegap.ch> writes:
Given that a zone_id is a) highly system dependent and b) only ever
meaningful for non-global addresses, I'm wondering what the use case for
storing them is.
I'm even wondering if 'fe80::1%1'::inet = 'fe80::1%2'::inet shouldn't
simply yield true. After all, it's the same (non-global) address.
Surely not? If the zone_ids didn't mean anything, why would the concept
even exist? ISTM that what you've got there is two different addresses
neither of which is reachable from outside the given machine --- but
within that machine, they are different.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 06/03/2016 12:14 AM, Tom Lane wrote:
Markus Wanner <markus@bluegap.ch> writes:
I'm even wondering if 'fe80::1%1'::inet = 'fe80::1%2'::inet shouldn't
simply yield true. After all, it's the same (non-global) address.Surely not? If the zone_ids didn't mean anything, why would the concept
even exist? ISTM that what you've got there is two different addresses
neither of which is reachable from outside the given machine --- but
within that machine, they are different.
You are right, both of these addresses actually are considered different
from 'fe80::1', i.e. I cannot ping6 fe80::1, but need to explicitly
specify an interface - either via an additional argument or with the
percent notation.
That's interesting news to me, as this means that only global IPv6
addresses can be stored in 128 bits. For non-global ones that's not
sufficient and 'fe80::1' isn't even considered a valid address by
itself. Checking the headers and manpages, the sockaddr_in6 sports an
uint32_t sin6_scope_id (as per RFC 2553).
Then, there also is the embedded form which uses the 2nd 16-bit word of
the IPv6 address to store the scope id, see for example "8.1.1.3 Scope
Index" in [0]FreeBSD dev handbook, Section 8.1.1.3: Scope Index: https://www.freebsd.org/doc/en/books/developers-handbook/ipv6.html. However, that document also clearly states: "When you
specify scoped address to the command line, NEVER write the embedded
form (such as ff02:1::1 or fe80:2::fedc)".
Given that interfaces are addressed by index internally, I'm pretty sure
the two representations are equivalent (i.e. if eth3 currently happens
to be the 7th interface , then 'fe80::1%eth3' resolves to 'fe80::1%7').
Considering that Postgres is not unlikely to write INET types to
permanent storage, its values should better survive a reboot. And while
I have some doubts about persistence of interface names, those clearly
have a higher chance of surviving a reboot compared to interface
indices. Therefore, I'd advocate resolving interface indices (if given)
to interface names using if_indextoname(3) and let INET types store only
names.
Assuming such an implementation, the following would hold (assuming an
example system as mentioned above):
'fe80::1%1'::inet != 'fe80::1%2'::inet
but:
'fe80::1%7'::inet = 'fe80::1%eth3'::inet
I also tend to deny scope ids for global addresses (like ping6), but
allow non-global ('fe80::1') addresses to be stored without an interface
name, as pre-existing applications may rely on that to work (unlike ping6).
Does that sound like a more reasonable plan?
Kind Regards
Markus Wanner
[0]: FreeBSD dev handbook, Section 8.1.1.3: Scope Index: https://www.freebsd.org/doc/en/books/developers-handbook/ipv6.html
https://www.freebsd.org/doc/en/books/developers-handbook/ipv6.html
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Markus Wanner <markus@bluegap.ch> writes:
Considering that Postgres is not unlikely to write INET types to
permanent storage, its values should better survive a reboot. And while
I have some doubts about persistence of interface names, those clearly
have a higher chance of surviving a reboot compared to interface
indices. Therefore, I'd advocate resolving interface indices (if given)
to interface names using if_indextoname(3) and let INET types store only
names.
What will you do on machines without if_indextoname()? More importantly,
on what basis do you conclude that the inet type will only be asked to
store link-local addresses that are currently valid on the local machine?
It is not very hard to think of applications where that wouldn't be the
case.
I think a better plan is just to store the zone string verbatim. It is
not our job to check its validity or try to determine which spellings
are equivalent.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 06/03/2016 06:55 PM, Tom Lane wrote:
More importantly,
on what basis do you conclude that the inet type will only be asked to
store link-local addresses that are currently valid on the local machine?
It is not very hard to think of applications where that wouldn't be the
case.
That's a good point.
I think a better plan is just to store the zone string verbatim. It is
not our job to check its validity or try to determine which spellings
are equivalent.
Agreed.
That leaves me wondering if it's really worth extending INET, though.
TEXT would be just fine to store a textual scope id. And it makes it
utterly clear that there's no magic involved.
Kind Regards
Markus Wanner
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Markus Wanner <markus@bluegap.ch> writes:
That leaves me wondering if it's really worth extending INET, though.
TEXT would be just fine to store a textual scope id. And it makes it
utterly clear that there's no magic involved.
True, but it would force users to disassemble and reassemble the
"address%scope" notation, which is kind of a pain.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Jun 3, 2016 at 5:02 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Markus Wanner <markus@bluegap.ch> writes:
That leaves me wondering if it's really worth extending INET, though.
TEXT would be just fine to store a textual scope id. And it makes it
utterly clear that there's no magic involved.True, but it would force users to disassemble and reassemble the
"address%scope" notation, which is kind of a pain.
Yeah, but what if somebody doesn't want to store scopes?
ff::1%fourscoreandsevenyearsagoourforefathersbroughtforthonthiscontinentanewnationconceivedinlibertyanddedicatedtothepropositionthatlallmenarecreatedequal
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
Yeah, but what if somebody doesn't want to store scopes?
[ shrug... ] We can invent a "strip_scope()" sort of function,
analogous to the weight-stripping function for tsvectors, or several
other examples. That's a really lame excuse for not being *able*
to store scopes.
ff::1%fourscoreandsevenyearsagoourforefathersbroughtforthonthiscontinentanewnationconceivedinlibertyanddedicatedtothepropositionthatlallmenarecreatedequal
I have not looked at the spec, but I wouldn't be surprised if there
were an upper limit on the length of valid scope names.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sat, Jun 4, 2016 at 1:50 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Yeah, but what if somebody doesn't want to store scopes?
[ shrug... ] We can invent a "strip_scope()" sort of function,
analogous to the weight-stripping function for tsvectors, or several
other examples. That's a really lame excuse for not being *able*
to store scopes.ff::1%fourscoreandsevenyearsagoourforefathersbroughtforthonthiscontinentanewnationconceivedinlibertyanddedicatedtothepropositionthatlallmenarecreatedequal
I have not looked at the spec, but I wouldn't be surprised if there
were an upper limit on the length of valid scope names.
I am not able to find any link that suggests the maximum length of the scope id.
From [1]https://en.wikipedia.org/wiki/IPv6_address#Link-local_addresses_and_zone_indices, I came to know that, how the scope id is formed in different operating
systems.
windows - fe80::3%1
unix systems - fe80::3%eth0
From [2]http://stackoverflow.com/questions/24932172/what-length-can-a-network-interface-name-have, the maximum interface name allowed in linux is 16 bytes and windows
256 bytes. Any way for windows the zone id is a number index to the interface.
I added another character array of 256 member into inet_struct as a last member
to store the zone id. Modified the inet_cidr_pton_ipv6 function to handle '%'.
Currently all the printable characters are treated as zone id's. I
will restrict this
to only alphabets and numbers.
Here I attached POC patch that adds the support of storing ipv6 with scope id
in the inet datatype.
Approach of adding the member to the structure and storing is fine or any better
way to handle the same? If we come to an conclusion on the approach, i will
add the zone support for everything and send the patch.
How about the following case, Do we treat them as same or different?
select 'fe80::%eth1'::inet = 'fe80::%ETH1'::inet;
fe80::%2/64 is only treated as the valid address but not other way as
fe80::/64%2.
Do we need to throw an error in this case or just ignore.
[1]: https://en.wikipedia.org/wiki/IPv6_address#Link-local_addresses_and_zone_indices
[2]: http://stackoverflow.com/questions/24932172/what-length-can-a-network-interface-name-have
Regards,
Hari Babu
Fujitsu Australia
Attachments:
ipv6_scopeid_poc.patchapplication/octet-stream; name=ipv6_scopeid_poc.patchDownload
diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c
index 9064eaf..dcb527f 100644
--- a/src/backend/utils/adt/inet_net_pton.c
+++ b/src/backend/utils/adt/inet_net_pton.c
@@ -435,6 +435,7 @@ inet_net_pton_ipv6(const char *src, u_char *dst)
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
#define NS_INADDRSZ 4
+#define NS_IN6SCOPESZ 256
static int
inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
@@ -442,13 +443,16 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
u_char tmp[NS_IN6ADDRSZ],
+ scopeid[NS_IN6SCOPESZ],
*tp,
*endp,
- *colonp;
+ *colonp,
+ *sp;
const char *xdigits,
*curtok;
int ch,
- saw_xdigit;
+ saw_xdigit,
+ saw_scopeid;
u_int val;
int digits;
int bits;
@@ -458,6 +462,8 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
memset((tp = tmp), '\0', NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
+
+ memset((sp = scopeid), '\0', NS_IN6SCOPESZ);
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
@@ -465,6 +471,7 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
goto enoent;
curtok = src;
saw_xdigit = 0;
+ saw_scopeid = 0;
val = 0;
digits = 0;
bits = -1;
@@ -513,6 +520,28 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
}
if (ch == '/' && getbits(src, &bits) > 0)
break;
+ if (ch == '%')
+ {
+ if (saw_scopeid)
+ goto enoent;
+
+ do
+ {
+ ch = *src++;
+ /* consider all printable characters */
+ if (32 <= ch && ch <= 127)
+ *sp++ = ch;
+ else
+ goto enoent;
+
+ if (sp - scopeid >= NS_IN6SCOPESZ)
+ goto enoent;
+ } while (*src != '\0' && *src != '/');
+
+ saw_scopeid = 1;
+ continue;
+ }
+
goto enoent;
}
if (saw_xdigit)
@@ -552,6 +581,7 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
* Copy out the result.
*/
memcpy(dst, tmp, NS_IN6ADDRSZ);
+ memcpy(dst + NS_IN6ADDRSZ, scopeid, NS_IN6SCOPESZ);
return (bits);
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 1f8469a..d3c4594 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -117,6 +117,24 @@ network_out(inet *src, bool is_cidr)
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
}
+ if (ip_scopeid_size(src))
+ {
+ char *ch = strchr(tmp, '/');
+
+ if (ch)
+ {
+ len = ch - tmp;
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_scopeid(src));
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
+ }
+ else
+ {
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_scopeid(src));
+ }
+ }
+
return pstrdup(tmp);
}
@@ -399,7 +417,11 @@ network_cmp_internal(inet *a1, inet *a2)
order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
if (order != 0)
return order;
- return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
+ order = bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
+ if (order != 0)
+ return order;
+
+ return pg_strcasecmp((const char *)ip_scopeid(a1), (const char *)ip_scopeid(a2));
}
return ip_family(a1) - ip_family(a2);
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index 2fe3ca8..7b52983 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -25,6 +25,7 @@ typedef struct
unsigned char family; /* PGSQL_AF_INET or PGSQL_AF_INET6 */
unsigned char bits; /* number of bits in netmask */
unsigned char ipaddr[16]; /* up to 128 bits of address */
+ unsigned char ipscopeid[256]; /* up to 255 bytes of interface name */
} inet_struct;
/*
@@ -75,15 +76,22 @@ typedef struct
#define ip_addr(inetptr) \
(((inet_struct *) VARDATA_ANY(inetptr))->ipaddr)
+#define ip_scopeid(inetptr) \
+ (((inet_struct *) VARDATA_ANY(inetptr))->ipscopeid)
+
#define ip_addrsize(inetptr) \
(ip_family(inetptr) == PGSQL_AF_INET ? 4 : 16)
+#define ip_scopeid_size(inetptr) \
+ (ip_family(inetptr) == PGSQL_AF_INET ? 0 \
+ : (strlen((const char *)((inet_struct *) VARDATA_ANY(inetptr))->ipscopeid)))
+
#define ip_maxbits(inetptr) \
(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
#define SET_INET_VARSIZE(dst) \
SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \
- ip_addrsize(dst))
+ ip_addrsize(dst) + ip_scopeid_size(dst))
/*
On 6/7/16 1:19 AM, Haribabu Kommi wrote:
How about the following case, Do we treat them as same or different?
select 'fe80::%eth1'::inet = 'fe80::%ETH1'::inet;
fe80::%2/64 is only treated as the valid address but not other way as
fe80::/64%2.
Do we need to throw an error in this case or just ignore.
I suspect questions like these are already solved in someone else's IP
address type/object/class implementation, and we could look there.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Haribabu,
On 07.06.2016 07:19, Haribabu Kommi wrote:
I have not looked at the spec, but I wouldn't be surprised if there
were an upper limit on the length of valid scope names.
Yeah, I didn't find any upper limit, either.
I am not able to find any link that suggests the maximum length of the scope id.
From [1], I came to know that, how the scope id is formed in different operating
systems.windows - fe80::3%1
unix systems - fe80::3%eth0
eth0 may well be interface number 1 on some machine, therefore being
equivalent. However, as discussed before, Postgres cannot know, so it
shouldn't bother.
I added another character array of 256 member into inet_struct as a last member
to store the zone id.
I haven't looked at the patch in detail, but zeroing or memcpy'ing those
256 bytes seems like overkill to me. I'd recommend to limit this to only
allocate and move around as many bytes as needed for the scope id.
Currently all the printable characters are treated as zone id's. I
will restrict this
to only alphabets and numbers.
I fear alphanumeric only is too restrictive. RFC 4007 only specifies
that the zone id "must not conflict with the delimiter character" and
leaves everything beyond that to the implementation (which seems too
loose, printable characters sounds about right to me...).
i will add the zone support for everything and send the patch.
What's currently missing?
How about the following case, Do we treat them as same or different?
select 'fe80::%eth1'::inet = 'fe80::%ETH1'::inet;
Let's be consistent in not interpreting the scope id in any way, meaning
those would be different. (After all, interfaces names seem to be case
sensitive - on my variant of Linux at the very least - i.e. ETH1 cannot
be found, while eth1 can be.)
fe80::%2/64 is only treated as the valid address but not other way as
fe80::/64%2.
Do we need to throw an error in this case or just ignore.
I didn't find any evidence for the second case being invalid; nor for it
being valid.
Note, however, that RFC 4007 only gives recommendations for textual
representations (with a "should" preference for the former).
It explicitly "does not specify how the format for non-global addresses
should be combined with the preferred format for literal IPv6 addresses".
Also note that RFC 2732 (Format for Literal IPv6 Addresses in URL's)
doesn't have the '%' sign in the set of reserved characters (not even in
the "unwise" one).
I'm starting to question if it's really wise to add the scope id to the
INET6 type...
[2] - http://stackoverflow.com/questions/24932172/what-length-can-a-network-interface-name-have
Note that the scope id doesn't necessarily have to be a network
interface name. Concluding there's at max 256 bytes, just because that's
the network interface name's max, doesn't seem correct. However, I agree
that's a reasonable limit for a scope id of the inet6 data type.
Kind Regards
Markus Wanner
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jun 29, 2016 at 6:31 AM, Markus Wanner <markus@bluegap.ch> wrote:
I added another character array of 256 member into inet_struct as a last member
to store the zone id.I haven't looked at the patch in detail, but zeroing or memcpy'ing those
256 bytes seems like overkill to me. I'd recommend to limit this to only
allocate and move around as many bytes as needed for the scope id.
Currently a static structure members are passed to the inet_net_pton function
to fill the data, to make it simple i added a member to store the zoneid in the
structure.
Currently all the printable characters are treated as zone id's. I
will restrict this
to only alphabets and numbers.I fear alphanumeric only is too restrictive. RFC 4007 only specifies
that the zone id "must not conflict with the delimiter character" and
leaves everything beyond that to the implementation (which seems too
loose, printable characters sounds about right to me...).
Ok. Currently the patch supports all printable characters, except '/' that is
reserved for specifying the bits.
How about the following case, Do we treat them as same or different?
select 'fe80::%eth1'::inet = 'fe80::%ETH1'::inet;
Let's be consistent in not interpreting the scope id in any way, meaning
those would be different. (After all, interfaces names seem to be case
sensitive - on my variant of Linux at the very least - i.e. ETH1 cannot
be found, while eth1 can be.)
Added a case sensitive comparison for zoneid.
fe80::%2/64 is only treated as the valid address but not other way as
fe80::/64%2.
Do we need to throw an error in this case or just ignore.I didn't find any evidence for the second case being invalid; nor for it
being valid.
Yes, that's correct. Second one invalid. Patch throws an error.
I'm starting to question if it's really wise to add the scope id to the
INET6 type...
I didn't find any major database that support inet datatype, which is good
for storing IP address. I feel it may be better to extend it to support new
IP enhancements.
Updated patch is attached. Docs and tests are pending.
Regards,
Hari Babu
Fujitsu Australia
Attachments:
ipv6_scopeid_1.patchapplication/octet-stream; name=ipv6_scopeid_1.patchDownload
diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c
index b8fa7d2..6a732d7 100644
--- a/src/backend/utils/adt/inet_net_pton.c
+++ b/src/backend/utils/adt/inet_net_pton.c
@@ -435,6 +435,7 @@ inet_net_pton_ipv6(const char *src, u_char *dst)
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
#define NS_INADDRSZ 4
+#define NS_IN6ZONESZ 256
static int
inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
@@ -442,13 +443,16 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
u_char tmp[NS_IN6ADDRSZ],
+ zoneid[NS_IN6ZONESZ],
*tp,
*endp,
- *colonp;
+ *colonp,
+ *zp;
const char *xdigits,
*curtok;
int ch,
- saw_xdigit;
+ saw_xdigit,
+ saw_zoneid;
u_int val;
int digits;
int bits;
@@ -458,6 +462,8 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
memset((tp = tmp), '\0', NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
+
+ memset((zp = zoneid), '\0', NS_IN6ZONESZ);
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
@@ -465,6 +471,7 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
goto enoent;
curtok = src;
saw_xdigit = 0;
+ saw_zoneid = 0;
val = 0;
digits = 0;
bits = -1;
@@ -513,6 +520,28 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
}
if (ch == '/' && getbits(src, &bits) > 0)
break;
+ if (ch == '%')
+ {
+ if (saw_zoneid)
+ goto enoent;
+
+ do
+ {
+ ch = *src++;
+ /* consider all printable characters */
+ if (32 <= ch && ch <= 127)
+ *zp++ = ch;
+ else
+ goto enoent;
+
+ if (zp - zoneid >= NS_IN6ZONESZ)
+ goto enoent;
+ } while (*src != '\0' && *src != '/');
+
+ saw_zoneid = 1;
+ continue;
+ }
+
goto enoent;
}
if (saw_xdigit)
@@ -552,6 +581,7 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
* Copy out the result.
*/
memcpy(dst, tmp, NS_IN6ADDRSZ);
+ memcpy(dst + NS_IN6ADDRSZ, zoneid, NS_IN6ZONESZ);
return (bits);
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 1f8469a..2c666d3 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -99,7 +99,7 @@ cidr_in(PG_FUNCTION_ARGS)
static char *
network_out(inet *src, bool is_cidr)
{
- char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN];
char *dst;
int len;
@@ -117,6 +117,24 @@ network_out(inet *src, bool is_cidr)
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
}
+ if (ip_contains_zoneid(src))
+ {
+ char *ch = strchr(tmp, '/');
+
+ if (ch)
+ {
+ len = ch - tmp;
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(src));
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
+ }
+ else
+ {
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(src));
+ }
+ }
+
return pstrdup(tmp);
}
@@ -152,6 +170,7 @@ network_recv(StringInfo buf, bool is_cidr)
{
inet *addr;
char *addrptr;
+ char *zoneidptr;
int bits;
int nb,
i;
@@ -188,6 +207,18 @@ network_recv(StringInfo buf, bool is_cidr)
for (i = 0; i < nb; i++)
addrptr[i] = pq_getmsgbyte(buf);
+ /* zoneid */
+ nb = pq_getmsgbyte(buf);
+ if (nb >= MAX_ZONEID_LEN)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("invalid length in external zoneid value"),
+ errhint("Max supported zone id length :%d", (MAX_ZONEID_LEN - 1))));
+
+ zoneidptr = (char *) ip_zoneid(addr);
+ for (i = 0; i < nb; i++)
+ zoneidptr[i] = pq_getmsgbyte(buf);
+
/*
* Error check: CIDR values must not have any bits set beyond the masklen.
*/
@@ -230,6 +261,7 @@ network_send(inet *addr, bool is_cidr)
{
StringInfoData buf;
char *addrptr;
+ char *zoneidptr;
int nb,
i;
@@ -244,6 +276,16 @@ network_send(inet *addr, bool is_cidr)
addrptr = (char *) ip_addr(addr);
for (i = 0; i < nb; i++)
pq_sendbyte(&buf, addrptr[i]);
+
+ /* zoneid */
+ nb = ip_zoneid_size(addr);
+ if (nb < 0)
+ nb = 0;
+ pq_sendbyte(&buf, nb);
+ zoneidptr = (char *) ip_zoneid(addr);
+ for (i = 0; i < nb; i++)
+ pq_sendbyte(&buf, zoneidptr[i]);
+
return pq_endtypsend(&buf);
}
@@ -399,7 +441,15 @@ network_cmp_internal(inet *a1, inet *a2)
order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
if (order != 0)
return order;
- return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
+ order = bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
+ if (order != 0)
+ return order;
+
+ if ((ip_family(a1) == PGSQL_AF_INET6)
+ && (ip_contains_zoneid(a1) || ip_contains_zoneid(a2)))
+ return strcmp((const char *) ip_zoneid(a1), (const char *) ip_zoneid(a2));
+
+ return 0;
}
return ip_family(a1) - ip_family(a2);
@@ -597,7 +647,7 @@ network_host(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_PP(0);
char *ptr;
- char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN];
/* force display of max bits, regardless of masklen... */
if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
@@ -610,6 +660,14 @@ network_host(PG_FUNCTION_ARGS)
if ((ptr = strchr(tmp, '/')) != NULL)
*ptr = '\0';
+ if (ip_contains_zoneid(ip))
+ {
+ int len;
+
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip));
+ }
+
PG_RETURN_TEXT_P(cstring_to_text(tmp));
}
@@ -623,7 +681,7 @@ network_show(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_PP(0);
int len;
- char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN];
if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
tmp, sizeof(tmp)) == NULL)
@@ -638,6 +696,24 @@ network_show(PG_FUNCTION_ARGS)
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
}
+ if (ip_contains_zoneid(ip))
+ {
+ char *ch = strchr(tmp, '/');
+
+ if (ch)
+ {
+ len = ch - tmp;
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip));
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
+ }
+ else
+ {
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip));
+ }
+ }
+
PG_RETURN_TEXT_P(cstring_to_text(tmp));
}
@@ -646,7 +722,7 @@ inet_abbrev(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_PP(0);
char *dst;
- char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN];
dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
ip_bits(ip), tmp, sizeof(tmp));
@@ -656,6 +732,25 @@ inet_abbrev(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format inet value: %m")));
+ if (ip_contains_zoneid(ip))
+ {
+ int len;
+ char *ch = strchr(tmp, '/');
+
+ if (ch)
+ {
+ len = ch - tmp;
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip));
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
+ }
+ else
+ {
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip));
+ }
+ }
+
PG_RETURN_TEXT_P(cstring_to_text(tmp));
}
@@ -664,7 +759,7 @@ cidr_abbrev(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_PP(0);
char *dst;
- char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN];
dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
ip_bits(ip), tmp, sizeof(tmp));
@@ -674,6 +769,25 @@ cidr_abbrev(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format cidr value: %m")));
+ if (ip_contains_zoneid(ip))
+ {
+ int len;
+ char *ch = strchr(tmp, '/');
+
+ if (ch)
+ {
+ len = ch - tmp;
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip));
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
+ }
+ else
+ {
+ len = strlen(tmp);
+ snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip));
+ }
+ }
+
PG_RETURN_TEXT_P(cstring_to_text(tmp));
}
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index 2fe3ca8..464fb0c 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -16,6 +16,8 @@
#include "fmgr.h"
+#define MAX_ZONEID_LEN 256
+
/*
* This is the internal storage format for IP addresses
* (both INET and CIDR datatypes):
@@ -25,6 +27,7 @@ typedef struct
unsigned char family; /* PGSQL_AF_INET or PGSQL_AF_INET6 */
unsigned char bits; /* number of bits in netmask */
unsigned char ipaddr[16]; /* up to 128 bits of address */
+ unsigned char ipzoneid[MAX_ZONEID_LEN]; /* up to 255 bytes of zone id name */
} inet_struct;
/*
@@ -75,15 +78,28 @@ typedef struct
#define ip_addr(inetptr) \
(((inet_struct *) VARDATA_ANY(inetptr))->ipaddr)
+#define ip_zoneid(inetptr) \
+ (((inet_struct *) VARDATA_ANY(inetptr))->ipzoneid)
+
#define ip_addrsize(inetptr) \
(ip_family(inetptr) == PGSQL_AF_INET ? 4 : 16)
+#define inet_addrsize(dst) \
+ (VARHDRSZ + offsetof(inet_struct, ipaddr) + ip_addrsize(dst))
+
+#define ip_zoneid_size(inetptr) \
+ (ip_family(inetptr) == PGSQL_AF_INET ? false \
+ : (strlen((const char *)((inet_struct *) VARDATA_ANY(inetptr))->ipzoneid)))
+
+#define ip_contains_zoneid(inetptr) \
+ (ip_family(inetptr) == PGSQL_AF_INET ? false \
+ : (VARSIZE_ANY(inetptr) > inet_addrsize(inetptr) ? true : false))
+
#define ip_maxbits(inetptr) \
(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
#define SET_INET_VARSIZE(dst) \
- SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \
- ip_addrsize(dst))
+ SET_VARSIZE(dst, inet_addrsize(dst) + ip_zoneid_size(dst))
/*
On 6/7/16 2:43 PM, Peter Eisentraut wrote:
On 6/7/16 1:19 AM, Haribabu Kommi wrote:
How about the following case, Do we treat them as same or different?
select 'fe80::%eth1'::inet = 'fe80::%ETH1'::inet;
fe80::%2/64 is only treated as the valid address but not other way as
fe80::/64%2.
Do we need to throw an error in this case or just ignore.I suspect questions like these are already solved in someone else's IP
address type/object/class implementation, and we could look there.
I have been doing some testing and reviewed the RFCs again. I'm having
some second thoughts about this.
I tried the IP address parsing modules in Perl, Python, Ruby, and they
all reject zone IDs.
The Java class java.net.Inet6Address accepts zone IDs, but only numbers
and names actually present on the local host.
RFC 4007 specifies that the zone IDs "should" not be used for global
addresses or the loopback address. The presented patch does not check
for that.
The original message in this thread mentioned an address "::1%0", so
that is not even valid. 0 is supposed to be the default zone, so one
could argue that that can be silently accepted.
A zone ID is only meaningful inside a node. So storing it in a table
without an associated node is meaningless. (compare: storing a time
with time zone without a date)
There are also questions about comparing addresses with zone IDs, such
as in the example at the very top. We don't have a good answer on
whether those addresses should be equal. (My OS seems to think that
interface names are case-insensitive.) Also, since there is no node
associated with the address, it's questionable what equal really means
anyway.
So I'm having doubts that the proposed change to allow any string to be
appended to any address is really sound. This could invite a lot of
shenanigans, where equality comparisons or range operations are
subverted, for example.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Nov 4, 2016 at 6:33 AM, Peter Eisentraut <
peter.eisentraut@2ndquadrant.com> wrote:
On 6/7/16 2:43 PM, Peter Eisentraut wrote:
On 6/7/16 1:19 AM, Haribabu Kommi wrote:
How about the following case, Do we treat them as same or different?
select 'fe80::%eth1'::inet = 'fe80::%ETH1'::inet;
fe80::%2/64 is only treated as the valid address but not other way as
fe80::/64%2.
Do we need to throw an error in this case or just ignore.I suspect questions like these are already solved in someone else's IP
address type/object/class implementation, and we could look there.I have been doing some testing and reviewed the RFCs again. I'm having
some second thoughts about this.I tried the IP address parsing modules in Perl, Python, Ruby, and they
all reject zone IDs.The Java class java.net.Inet6Address accepts zone IDs, but only numbers
and names actually present on the local host.
Thanks for the review and analyzing other modules behavior.
RFC 4007 specifies that the zone IDs "should" not be used for global
addresses or the loopback address. The presented patch does not check
for that.
I will add the check.
The original message in this thread mentioned an address "::1%0", so
that is not even valid. 0 is supposed to be the default zone, so one
could argue that that can be silently accepted.
Yes, I agree that default zone is the main use case of the original thread.
From the RFC 4007, the default zone is used for the global addresses,
This may be the main use case with zone id. How about currently just
ignoring it and store the actual IP address with the attached patch and
handle the rest of the actual zone id support later once the it gets
properly standardized?
A zone ID is only meaningful inside a node. So storing it in a table
without an associated node is meaningless. (compare: storing a time
with time zone without a date)There are also questions about comparing addresses with zone IDs, such
as in the example at the very top. We don't have a good answer on
whether those addresses should be equal. (My OS seems to think that
interface names are case-insensitive.) Also, since there is no node
associated with the address, it's questionable what equal really means
anyway.So I'm having doubts that the proposed change to allow any string to be
appended to any address is really sound. This could invite a lot of
shenanigans, where equality comparisons or range operations are
subverted, for example.
Currently there is lack of information to decide for the above problems from
the RFC, may be lets wait for the standardization to be clear and then
support zone id by adding minimal support for default zone?
Regards,
Hari Babu
Fujitsu Australia
Attachments:
0001-INET-ipv6-default-zone-support.patchapplication/octet-stream; name=0001-INET-ipv6-default-zone-support.patchDownload
From 688f0357a581b6711c8e2b2410e43d31b78d35ce Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Mon, 7 Nov 2016 16:36:14 +1100
Subject: [PATCH] INET ipv6 default zone support
::1%0 is the default zone that is used for the global addresses
Add the support for INET to ignore the default zone and store
the actual IP address.
Once the Zone id related stuff gets standardized, lets support
storing all zoneid data as part of IP address.
---
src/backend/utils/adt/inet_net_pton.c | 12 +++++++++++-
src/test/regress/expected/inet.out | 21 +++++++++++++++++++--
src/test/regress/sql/inet.sql | 10 ++++++++--
3 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c
index b8fa7d2..368f369 100644
--- a/src/backend/utils/adt/inet_net_pton.c
+++ b/src/backend/utils/adt/inet_net_pton.c
@@ -513,6 +512,17 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size)
}
if (ch == '/' && getbits(src, &bits) > 0)
break;
+ if (ch == '%')
+ {
+ if (*src == '\0')
+ goto enoent;
+
+ ch = *src++;
+ if (ch != '0' || (*src != '\0' && *src != '/'))
+ goto enoent;
+ continue;
+ }
+
goto enoent;
}
if (saw_xdigit)
diff --git a/src/test/regress/expected/inet.out b/src/test/regress/expected/inet.out
index be9427e..55d0867 100644
--- a/src/test/regress/expected/inet.out
+++ b/src/test/regress/expected/inet.out
@@ -19,8 +19,8 @@ INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16');
INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
-INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
-INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1%0', '10:23::f1%0/64');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000%0/113', '10:23::ffff%0');
INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
-- check that CIDR rejects invalid input:
INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/30', '192.168.1.226');
@@ -43,6 +43,23 @@ ERROR: invalid cidr value: "ffff:ffff:ffff:ffff::/24"
LINE 1: INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:fff...
^
DETAIL: Value has bits set to right of mask.
+-- check for ipv6 default zoneid rejects
+INSERT INTO INET_TBL (c, i) VALUES ('::1%01', '::1%0');
+ERROR: invalid input syntax for type cidr: "::1%01"
+LINE 1: INSERT INTO INET_TBL (c, i) VALUES ('::1%01', '::1%0');
+ ^
+INSERT INTO INET_TBL (c, i) VALUES ('::1%0', '::1%01');
+ERROR: invalid input syntax for type inet: "::1%01"
+LINE 1: INSERT INTO INET_TBL (c, i) VALUES ('::1%0', '::1%01');
+ ^
+INSERT INTO INET_TBL (c, i) VALUES ('::1%0%1', '::1%0%1');
+ERROR: invalid input syntax for type cidr: "::1%0%1"
+LINE 1: INSERT INTO INET_TBL (c, i) VALUES ('::1%0%1', '::1%0%1');
+ ^
+INSERT INTO INET_TBL (c, i) VALUES ('::1%', '::1/24%');
+ERROR: invalid input syntax for type cidr: "::1%"
+LINE 1: INSERT INTO INET_TBL (c, i) VALUES ('::1%', '::1/24%');
+ ^
SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
ten | cidr | inet
-----+--------------------+------------------
diff --git a/src/test/regress/sql/inet.sql b/src/test/regress/sql/inet.sql
index 880e115..57fb411 100644
--- a/src/test/regress/sql/inet.sql
+++ b/src/test/regress/sql/inet.sql
@@ -20,8 +20,8 @@ INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16');
INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
-INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
-INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1%0', '10:23::f1%0/64');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000%0/113', '10:23::ffff%0');
INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
-- check that CIDR rejects invalid input:
INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/30', '192.168.1.226');
@@ -29,6 +29,12 @@ INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
-- check that CIDR rejects invalid input when converting from text:
INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/30'), '192.168.1.226');
INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
+-- check for ipv6 default zoneid rejects
+INSERT INTO INET_TBL (c, i) VALUES ('::1%01', '::1%0');
+INSERT INTO INET_TBL (c, i) VALUES ('::1%0', '::1%01');
+INSERT INTO INET_TBL (c, i) VALUES ('::1%0%1', '::1%0%1');
+INSERT INTO INET_TBL (c, i) VALUES ('::1%', '::1/24%');
+
SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
-- now test some support functions
--
2.7.4.windows.1
On 11/7/16 1:13 AM, Haribabu Kommi wrote:
Yes, I agree that default zone is the main use case of the original thread.
From the RFC 4007, the default zone is used for the global addresses,
This may be the main use case with zone id. How about currently just
ignoring it and store the actual IP address with the attached patch and
handle the rest of the actual zone id support later once the it gets
properly standardized?
Well, according to the RFC, the default zone is 0 "typically", which is
a very weak requirement. So just ignoring it is probably also not right.
So far we have only heard one use case for any of this, which is someone
wanting to store ::1%0, which is not even a valid address according to
that same RFC. So this is all on very weak ground.
I think we should just forget about this. It's all a bit too dubious.
The only thing that might be useful and not questionable is a function
that takes text input and produces a zone-free address and a the zone ID
as separate return values. (Or perhaps two functions, one for each
component.)
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Nov 9, 2016 at 9:02 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:
On 11/7/16 1:13 AM, Haribabu Kommi wrote:
Yes, I agree that default zone is the main use case of the original thread.
From the RFC 4007, the default zone is used for the global addresses,
This may be the main use case with zone id. How about currently just
ignoring it and store the actual IP address with the attached patch and
handle the rest of the actual zone id support later once the it gets
properly standardized?Well, according to the RFC, the default zone is 0 "typically", which is
a very weak requirement. So just ignoring it is probably also not right.So far we have only heard one use case for any of this, which is someone
wanting to store ::1%0, which is not even a valid address according to
that same RFC. So this is all on very weak ground.I think we should just forget about this. It's all a bit too dubious.
+1.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
On Wed, Nov 9, 2016 at 9:02 AM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:On 11/7/16 1:13 AM, Haribabu Kommi wrote:
Yes, I agree that default zone is the main use case of the original thread.
From the RFC 4007, the default zone is used for the global addresses,
This may be the main use case with zone id. How about currently just
ignoring it and store the actual IP address with the attached patch and
handle the rest of the actual zone id support later once the it gets
properly standardized?
Well, according to the RFC, the default zone is 0 "typically", which is
a very weak requirement. So just ignoring it is probably also not right.
So far we have only heard one use case for any of this, which is someone
wanting to store ::1%0, which is not even a valid address according to
that same RFC. So this is all on very weak ground.
I think we should just forget about this. It's all a bit too dubious.
+1.
Agreed, let's wait until more standardization emerges. Anything we do now
risks painting ourselves into a corner, and there is not so much demand
for a feature in this area that we need to do something about it Right Now.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers