2011-02-09 34 views
14

En Oracle, ¿cuál es el tipo de datos o la técnica apropiada para representar direcciones de red, qué direcciones pueden ser IPv4 o IPv6?Representación de direcciones IPv4/IPv6 en Oracle

Antecedentes: Estoy convertir una actividad de la red grabación de mesa, construido con los PostgreSQL inet data type para mantener las direcciones tanto v4 y v6 en la misma tabla.

Sin embargo, ninguna fila contiene las direcciones v4 y v6. (Es decir, un registro es ya sea de pila v4 de una máquina, o la pila de v6 de una máquina.)

+1

¿Hace agregaciones/búsqueda en esa tabla por dirección IP? ¿Cuántas filas espera tener en 1 año? – jachguate

+0

Agregaciones y búsqueda por IP, sí. Número de filas, posiblemente cientos de millones. (¿Tiene recomendaciones pequeñas/medianas/grandes?) – pilcrow

+0

por favor, use la notación @jachguate en sus comentarios si quiere que me notifiquen al respecto. Por lo que dices, creo que el mejor enfoque es el primero de la respuesta de @Alain. – jachguate

Respuesta

14

En Oracle, ¿cuál es el tipo de datos apropiado o técnica para que representa las direcciones de red, que direcciones pueden estar IPv4 o IPv6

Hay dos enfoques:

  1. que almacenan solamente.
  2. almacenar la representación convencional

Para almacenar solamente. Una dirección IPV4 debe ser un número entero (32 bits son suficientes). Para IP V6, 128 bits, INTEGER (que es similar al Número (38)) hará. Por supuesto, eso es almacenamiento. Ese enfoque considera que la representación es un asunto de la aplicación.

Si se toma la estrategia opuesta, de almacenar la representación convencional, se debe asegurar que las direcciones IP V4 e IPV6 tengan solo una representación convencional (cadena). Es bien conocido por ipV4. En cuanto a IPV6, también hay un formato estándar.

Mi preferencia va a la primera estrategia. En el peor de los casos, puede adoptar un enfoque híbrido (aunque no sea ácido) y almacenar tanto la representación binaria como la ascii una al lado de la otra con "prioridad" al valor binario.

Ninguna fila contiene las direcciones v4 y v6 , sin embargo.

La representación estándar de una dirección IPV4 en formato IPV6 es: ::ffff:192.0.2.128.

No conozco el contexto, pero me gustaría reservar 2 columnas, una para IPV4 y la otra para una dirección ipV6 distinta.

actualización
Siguiendo un buen comentario por @ sleepyMonad de, me gustaría señalar que en lugar del número tipo de datos es preferible utilizar el tipo de datos entero, que estará feliz de dar cabida a la más alta posible valor que se puede expresar con un entero de 128 bits 'ff ... ff' (que necesitaría dígitos decimales). 38 es la potencia más alta de diez que va de 0 a 9 que se puede codificar en 128 bits, pero aún se puede insertar el valor máximo sin signo para 2 ** 128 - 1 (decimal 340282366920938463463374607431768211455). Aquí hay una pequeña prueba para ilustrar esta posibilidad.

create table test (
    id integer primary key, 
    ipv6_address_bin INTEGER); 

-- Let's enter 2**128 - 1 in the nueric field 
insert into test (id, ipv6_address_bin) values (1, to_number ('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')) ; 

-- retrieve it to make sure it's not "truncated". 
select to_char (ipv6_address_bin, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') from test where id = 1 ; 
-- yields 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' 

select to_char (ipv6_address_bin) from test where id = 1 ; 
-- yields 340282366920938463463374607431768211455 

select LOG(2, ipv6_address_bin) from test where id = 1 ; 
-- yields 128 

select LOG(10, ipv6_address_bin) from test where id = 1 ; 
-- yields > 38 
+0

Definitivamente de acuerdo con columnas separadas para valores IPv4 e IPv6. –

+0

¿También podría restringir las columnas v4 y v6 para que una y solo una de ellas tuviera que ser NULL? (Algo como 'CHECK ((src_v4 IS NULL y src_v6 IS NOT NULL) o (src_v4 IS NOT NULL y src_v6 IS NULL))')? – pilcrow

+0

Todo depende de lo que se supone que contienen los datos de la tabla. Asumiendo que la dirección IP no es la clave (parece obvio por su pregunta), y está rastreando usuarios, entonces mantendría la puerta abierta porque la gente podría conectarse desde varias direcciones IP. Si en cambio, su tabla rastrea los eventos de conexión en lugar de los usuarios (presumiblemente con una relación uno a n), entonces sí, porque se conecta en IP v4 o en V6 (aunque, como dije, las direcciones IP v4 tienen una representación V6) . –

3

@Alain Pannetier (porque no puedo comentar todavía): Los mapas de tipo de datos de entero a ANSI NÚMERO (38) en Oracle de acuerdo con http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements001.htm#i54335. Debajo de la tabla se encuentra la información de que NUMBER solo proporciona una precisión binaria de 126 bits que no es suficiente para una dirección IPv6 de 128 bits. El valor máximo podría almacenarse bien, pero habrá direcciones que se redondearán a la siguiente más baja.

El formato numérico interno es REDONDO ((longitud (p) + s)/2)) + 1 (http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#i16209).

Actualización: Después de perder el tiempo con el tema de nuevo he encontrado ahora una solución que permite consultas de alto rendimiento de las redes que contienen una dirección IPv6: almacenar las direcciones IPv6 y máscaras de subred en RAW (16) columnas y compara utilizando UTL_RAW.BIT_AND:

SELECT name, DECODE(UTL_RAW.BIT_AND('20010DB8000000000000000000000001', ipv6_mask), ipv6_net, 1, 0) 
FROM ip_net 
WHERE ipv6_net IS NOT NULL; 
1

la documentación de Oracle hace INTEGER estado es un alias para NÚMERO (38), pero eso es probablemente un error, porque el párrafo anterior se afirma:

NÚMERO (p, s) donde: pi s la precisión ... Oracle garantiza la portabilidad de números con una precisión de hasta 20 dígitos base-100, que es equivalente a 39 o 40 dígitos decimales, dependiendo de la posición del punto decimal.

Entonces NUMBER puede almacenar de 39 a 40 dígitos, y INTEGER es probablemente un alias para NUMBER (precisión máxima) en lugar de NUMBER (38). Es por eso que el ejemplo proporcionado funciona (y funciona si cambia INTEGER a NUMBER).

+0

¿Cómo responde eso la pregunta? – wallyk

6

Guárdelo en RAW.

RAW es la matriz de bytes de longitud variable, por lo que ....

  • simplemente tratan el IPv4 como una matriz de 4 bytes
  • y IPv6 como una matriz de 16 bytes

... y almacena cualquiera de ellos directamente en RAW (16).


RAW puede ser indexado, ser un PK, UNIQUE o FOREIGN KEY, por lo que puede hacer lo que normalmente podría con VARCHAR2 o INT/NÚMERO/decimal, pero con menos sobrecarga de conversión y almacenamiento.

Para ilustrar la sobrecarga de almacenamiento de INT sobre RAW, considere el siguiente ejemplo:

CREATE TABLE IP_TABLE (
    ID INT PRIMARY KEY, 
    IP_RAW RAW(16), 
    IP_INT INT 
); 

INSERT INTO IP_TABLE (ID, IP_RAW, IP_INT) VALUES (
    1, 
    HEXTORAW('FFFFFFFF'), 
    TO_NUMBER('FFFFFFFF', 'XXXXXXXX') 
); 

INSERT INTO IP_TABLE (ID, IP_RAW, IP_INT) VALUES (
    2, 
    HEXTORAW('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'), 
    TO_NUMBER('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') 
); 

SELECT VSIZE(IP_RAW), VSIZE(IP_INT), IP_TABLE.* FROM IP_TABLE; 

El resultado (bajo Oracle 10.2):

table IP_TABLE created. 
1 rows inserted. 
1 rows inserted. 
VSIZE(IP_RAW)   VSIZE(IP_INT)   ID      IP_RAW       IP_INT     
---------------------- ---------------------- ---------------------- -------------------------------- ---------------------- 
4      6      1      FFFFFFFF       4294967295    
16      21      2      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 340282366920938463463374607431768211455 
+0

+1 Me gusta el enfoque RAW. – pilcrow

1

También es posible usar un objeto de Oracle personalizado.

SQL>set SERVEROUTPUT on 
SQL>drop table test; 

Table dropped. 

SQL>drop type body inaddr; 

Type body dropped. 

SQL>drop type inaddr; 

Type dropped. 

SQL>create type inaddr as object 
    2 (/* TODO enter attribute and method declarations here */ 
    3 A number(5), 
    4 B number(5), 
    5 C number(5), 
    6 D number(5), 
    7 E number(5), 
    8 F number(5), 
    9 G number(5), 
10 H NUMBER(5), 
11 MAP MEMBER FUNCTION display RETURN VARCHAR2, 
12 MEMBER FUNCTION toString(SELF IN INADDR , CONTRACT BOOLEAN DEFAULT TRUE) RETURN VARCHAR2, 
13 CONSTRUCTOR FUNCTION INADDR(SELF IN OUT NOCOPY INADDR, INADDRASSTRING VARCHAR2) RETURN SELF AS RESULT 
14 
15 ) NOT FINAL; 
16/

SP2-0816: Type created with compilation warnings 

SQL> 
SQL> 
SQL>CREATE TYPE BODY INADDR AS 
    2 
    3 MAP MEMBER FUNCTION display RETURN VARCHAR2 
    4 IS BEGIN 
    5 return tostring(FALSE); 
    6 END; 
    7 
    8 
    9 MEMBER FUNCTION TOSTRING(SELF IN INADDR , CONTRACT BOOLEAN DEFAULT TRUE) RETURN VARCHAR2 IS 
10 IP4 VARCHAR2(6) := 'FM990'; 
11 ip6 varchar2(6) := 'FM0XXX'; 
12 BEGIN 
13 IF CONTRACT THEN 
14 ip6 := 'FMXXXX'; 
15 end if; 
16 
17 IF CONTRACT AND A =0 AND B=0 AND C = 0 AND D=0 AND E =0 AND F = 65535 THEN --ipv4 
18  RETURN '::FFFF:'||TO_CHAR(TRUNC(G/256),'FM990.')||TO_CHAR(MOD(G,256),'FM990.')||TO_CHAR(TRUNC(H/256),'FM990.')||TO_CHAR(MOD(H,256),'FM990'); 
19 ELSE 
20  RETURN 
21 TO_CHAR(A,ip6)||':'|| 
22 TO_CHAR(B,IP6)||':'|| 
23 TO_CHAR(C,ip6)||':'|| 
24 TO_CHAR(D,ip6)||':'|| 
25 TO_CHAR(E,ip6)||':'|| 
26 TO_CHAR(F,ip6)||':'|| 
27 TO_CHAR(G,ip6)||':'|| 
28 TO_CHAR(H,ip6); 
29 end if; 
30 end; 
31 
32  CONSTRUCTOR FUNCTION inaddr(SELF IN OUT NOCOPY inaddr, inaddrasstring VARCHAR2) 
33         RETURN SELF AS RESULT IS 
34  begin 
35   if instr(inaddrasstring,'.') > 0 then 
36   --ip4 
37 null; 
38    a := 0; 
39    B := 0; 
40    C := 0; 
41    D := 0; 
42    E := 0; 
43    F := TO_NUMBER('FFFF', 'XXXX'); 
44    G := TO_NUMBER(TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,1,'i',1),'999'),'FM0X') 
45 ||TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,2,'i',1),'999'),'FM0X') 
46 ,'XXXX'); 
47    h := TO_NUMBER(TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,3,'i',1),'999'),'FM0X') 
48 ||TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3})',1,4,'i',1),'999'),'FM0X') 
49 ,'XXXX'); 
50 
51   ELSIF instr(inaddrasstring,':') > 0 then 
52    --ip6 
53    a := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,1,'i',1),'XXXX'); 
54    b := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,2,'i',1),'XXXX'); 
55    c := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,3,'i',1),'XXXX'); 
56    d := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,4,'i',1),'XXXX'); 
57    E := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,5,'i',1),'XXXX'); 
58    f := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,6,'i',1),'XXXX'); 
59    g := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,7,'i',1),'XXXX'); 
60    H := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,8,'i',1),'XXXX'); 
61   end if; 
62 
63   RETURN; 
64  END; 
65 end; 
66/

Type body created. 

SQL> 
SQL>create table test 
    2 (id integer primary key, 
    3 address inaddr); 

Table created. 

SQL> 
SQL>select * from test; 

no rows selected 

SQL> 
SQL> 
SQL>insert into test values (1, INADDR('fe80:0000:0000:0000:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (2, INADDR('192.0.2.128')); 

1 row created. 

SQL>insert into test values (3, INADDR('20.0.20.1')); 

1 row created. 

SQL>insert into test values (4, INADDR('fe80:0001:0002:0003:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>insert into test values (5, INADDR('fe80:0003:0002:0003:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (6, INADDR('fe80:0003:0001:0003:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (7, INADDR('fe80:0003:0001:0003:0202:b3ff:fe1e:8328')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (8, INADDR('dead:beef:f00d:cafe:dea1:aced:b00b:1234')); 

1 row created. 

SQL> 
SQL>COLUMN INET_ADDRESS_SHORT FORMAT A40 
SQL>column inet_address_full format a40 
SQL> 
SQL>select t.address.toString() inet_address_short, t.address.display() inet_address_full 
    2 from test T 
    3 order by t.address ; 

INET_ADDRESS_SHORT      INET_ADDRESS_FULL 
---------------------------------------- ---------------------------------------- 
::FFFF:20.0.20.1       0000:0000:0000:0000:0000:FFFF:1400:1401 
::FFFF:192.0.2.128      0000:0000:0000:0000:0000:FFFF:C000:0280 
DEAD:BEEF:F00D:CAFE:DEA1:ACED:B00B:1234 DEAD:BEEF:F00D:CAFE:DEA1:ACED:B00B:1234 
FE80:0:0:0:202:B3FF:FE1E:8329   FE80:0000:0000:0000:0202:B3FF:FE1E:8329 
FE80:1:2:3:202:B3FF:FE1E:8329   FE80:0001:0002:0003:0202:B3FF:FE1E:8329 
FE80:3:1:3:202:B3FF:FE1E:8328   FE80:0003:0001:0003:0202:B3FF:FE1E:8328 
FE80:3:1:3:202:B3FF:FE1E:8329   FE80:0003:0001:0003:0202:B3FF:FE1E:8329 
FE80:3:2:3:202:B3FF:FE1E:8329   FE80:0003:0002:0003:0202:B3FF:FE1E:8329 

8 rows selected. 

SQL>spool off 

acabo de poner esto juntos en la última hora (y enseñó a mí mismo objetos al mismo tiempo) así que estoy seguro de que puede ser mejorado.si hago cambios voy a repost aquí

1

yo preferiría tienda de direcciones IP solo en cadena, en formato, devuelto por SYS_CONTEXT ('USERENV', 'DIRECCIÓN_IP')

En refference de SYS_CONTEXT en 11g son describió solo la longitud del valor de retorno predeterminado como 256 bytes y no describe el tamaño del valor de retorno para el contexto 'IP_ADDRESS'.

En el documento Oracle Database and IPv6 Statement of Direction describe:

Oracle Database 11g Release 2 apoya la dirección IPv6 estándar notaciones especificados por RFC2732. Una dirección IP de 128 bits es generalmente representada como 8 grupos de 4 dígitos hexadecimales, con el símbolo ":" como el separador de grupo . Los ceros iniciales en cada grupo se eliminan. Para el ejemplo , 1080: 0: 0: 0: 8: 800: 200C: 417A sería una dirección IPv6 válida. Un o más campos cero consecutivos opcionalmente se pueden comprimir con el separador "::". Por ejemplo, 1080 :: 8: 800: 200C: 417A.

De esta notas que prefieren hacer columna DIRECCIÓN_IP varchar2 (39) para permitir tienda 8 grupo de 4 dígitos y 7 separadores entre estos grupos.

Cuestiones relacionadas