2009-06-10 20 views
8

Estoy tratando de configurar el DF (no fragmentar el indicador) para enviar paquetes usando UDP.¿Cómo se configura el indicador do not fragment (DF) en un socket?

Mirando el libro de Richard Steven Volume 1 Unix Network Programming; The Sockets Networking API, no puedo encontrar cómo configurar esto.

Sospecho que lo haría con setsockopt(), pero no se puede encontrar en la tabla de la página 193.

Para sugerir cómo se hace esto.

Respuesta

18

lo haces con la llamada setsockopt(), mediante el uso de la opción IP_DONTFRAG ::

int val = 1; 
setsockopt(sd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val)); 

Here's una página que explica esto con más detalle.

Para Linux, parece que tiene que utilizar la opción IP_MTU_DISCOVER con el valor IP_PMTUDISC_DO (o IP_PMTUDISC_DONT para apagarlo):

int val = IP_PMTUDISC_DO; 
setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); 

no he probado esto, sólo se veía en los archivos de cabecera y un poco de una búsqueda web, así que tendrás que probarlo.

En cuanto a si hay otra forma de la bandera DF se podría establecer:

I find nowhere in my program where the "force DF flag" is set, yet tcpdump suggests it is. Is there any other way this could get set?

De esta excelente página here:

IP_MTU_DISCOVER: Sets or receives the Path MTU Discovery setting for a socket. When enabled, Linux will perform Path MTU Discovery as defined in RFC 1191 on this socket. The don't fragment flag is set on all outgoing datagrams. The system-wide default is controlled by the ip_no_pmtu_disc sysctl for SOCK_STREAM sockets, and disabled on all others. For non SOCK_STREAM sockets it is the user's responsibility to packetize the data in MTU sized chunks and to do the retransmits if necessary. The kernel will reject packets that are bigger than the known path MTU if this flag is set (with EMSGSIZE).

Esto me parece que puede establecer el valor predeterminado de todo el sistema utilizando sysctl:

sysctl ip_no_pmtu_disc 

vuelve "error: "ip_no_pmtu_disc" is an unknown key" en mi sistema, pero puede configurarse en el suyo. Aparte de eso, no tengo conocimiento de nada más (aparte de setsockopt() como se mencionó anteriormente) que puede afectar la configuración.

+0

¿Qué nivel es el debajo, IPPROTO_IP? – WilliamKF

+0

¿Hay alguna otra manera de que esto se establezca? Supongo que, por defecto, el DF está apagado, ¿verdad? – WilliamKF

+0

Creo que está desactivado por defecto, pero puede usar un getsockopt (sorprendentemente :-) para obtener su valor actual. – paxdiablo

3

Si está trabajando en Userland con la intención de eludir la pila de red Kernel y así construir sus propios paquetes y encabezados y entregarlos a un módulo Kernel personalizado, hay una mejor opción que setsockopt().

En realidad, puede establecer el indicador DF al igual que cualquier otro campo de struct iphdr definido en linux/ip.h. Los indicadores de IP de 3 bits son, de hecho, parte del miembro frag_off (Compensación de Fragmentos) de la estructura.

Cuando lo piense bien, tiene sentido agrupar estas dos cosas ya que las banderas están relacionadas con la fragmentación. De acuerdo con el RFC-791, la sección que describe la estructura del encabezado IP indica que el Desfase de Fragmentos es de 13 bits de longitud y hay tres indicadores de 1 bit. El miembro frag_off es del tipo __be16, que puede contener 13 + 3 bits.

Para resumir, he aquí una solución:

struct iphdr ip; 
ip.frag_off |= ntohs(IP_DF); 

Estamos aquí exactamente estableciendo el bit DF utilizando la máscara diseñada IP_DF-por-que-especial-propósito.

IP_DF se define en net/ip.h (cabeceras del núcleo, por supuesto), mientras que struct iphdr se define en linux/ip.h.

0

Acepto la respuesta de paxdiablo.

  • setsockopt (sockfd, IPPROTO_IP, IP_PMTU_DISCOVER, & val, sizeof (val))
#define IP_PMTUDISC_DONT 0 /* Never send DF frames. */ 
#define IP_PMTUDISC_WANT 1 /* Use per route hints. */ 
#define IP_PMTUDISC_DO  2 /* Always DF. */ 
#define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu. */ 
  • ip_no_pmtu_disc en las fuentes del núcleo:

if (ipv4_config.no_pmtu_disc) inet->pmtudisc = IP_PMTUDISC_DONT; else inet->pmtudisc = IP_PMTUDISC_WANT;

Cuestiones relacionadas