UDP数据包可以分段为几个较小的数据包

时间:2016-08-02 14:33:56

标签: networking udp ip mtu

如果UDP数据包超过MTU,是否可以将其分段为几个较小的数据包?似乎MTU碎片是关于IP层的,所以我认为它可以。

如果是,建议的最大值是多少。通过UDP发送的数据包大小以避免碎片,为什么?

2 个答案:

答案 0 :(得分:3)

如果IP数据报大于MTU,则可以对其进行分段。是否包含UDP,TCP,ICMP等并不重要。

大多数以太网网络支持1500字节MTU。 IPv4报头为20字节,UDP报头为8字节,UDP报文的有效载荷不应大于1500 - 20 - 8 = 1472字节,以避免碎片。

假设数据包中不存在IP选项。如果是这样,有效载荷将需要小于它的负载。

这仅适用于IPv4。 IPv6不支持分段。

您还可以看到this question regarding MTU for UDP

答案 1 :(得分:0)

如果您希望以特定大小分片数据包,以下示例将很有帮助。

#define UDP_FRAG_1024 1024

static int udp_raw_socket = -1;
static int udp_ip_iden = 1234;

int udp_frag1024_sendto(int s, caddr_t buf, int buf_len, int flags, struct sockaddr *to, int to_len)
{
    /* You must be in the sudoers files. */

    struct sockaddr_in sin;
    struct sockaddr_in *ptr_din = (struct sockaddr_in *)to;
    int sin_len = sizeof(sin);
    unsigned char packet[1500];
    short packet_len;
    short ip_iden;
    int sent1;
    int sent2;

    if (udp_raw_socket == -1) {
        int opt_val = 1;
        int *ptr_opt_val = &opt_val;

        udp_raw_socket = socket(AF_INET,SOCK_RAW,IPPROTO_RAW);

        (void)setsockopt(udp_raw_socket,IPPROTO_IP,IP_HDRINCL,(char *)ptr_opt_val,sizeof(opt_val));
    }

    if (buf_len > 2*UDP_FRAG_1024) {
        printf("buf_len %d not supported.\n",buf_len);
        return -1;
    }
    else if (buf_len <= UDP_FRAG_1024) {
        return sendto( s, buf, buf_len, flags, to, to_len );
    }
    else {
        ip_iden = udp_ip_iden++;

        (void)getsockname(s,(struct sockaddr *)&sin,&sin_len);

        /* 1st framentation - IP header */

        packet_len = 20 + 8 + UDP_FRAG_1024;

        packet[0] = 0x45; /* ver and header length */
        packet[1] = 0x00; /* tos */
        *(short *)&packet[2] = htons(packet_len);
        *(short *)&packet[4] = htons(ip_iden);
        packet[6] = 0x20; packet[7] = 0x00; /* flag */
        packet[8] = 0x40; /* ttl */
        packet[9] = 0x11; /* udp */
        packet[10] = 0x00; packet[11] = 0x00;/* checksum */
        memcpy( &packet[12], &sin.sin_addr.s_addr, 4 );
        memcpy( &packet[16], &ptr_din->sin_addr.s_addr, 4 );

        /* 1st framentation - UDP header */

        memcpy( &packet[20], &sin.sin_port, 2 );
        memcpy( &packet[22], &ptr_din->sin_port, 2 );
        *(short *)&packet[24] = htons(8+buf_len);
        packet[26] = 0x00; packet[27] = 0x00; /* checksum */

        /* 1st framentation - payload */

        memcpy( &packet[28], buf, UDP_FRAG_1024 );

        sent1 = sendto(udp_raw_socket,packet,packet_len,0,to,sizeof(struct sockaddr_in));

        /* 2nd framentation */

        packet_len = 20 + buf_len - UDP_FRAG_1024;

        *(short *)&packet[2] = htons(packet_len);
        packet[6] = 0x00; packet[7] = (8+UDP_FRAG_1024)/8; /* flag:0x81*8=129*8=1032=8+1024 */
        packet[10] = 0x00; packet[11] = 0x00; /* checksum */

        memcpy( &packet[20], buf+UDP_FRAG_1024, buf_len-UDP_FRAG_1024 );

        sent2 = sendto(udp_raw_socket,packet,packet_len,0,to,sizeof(struct sockaddr_in));
    }

    return ((sent1 - 20 - 8) + (sent2 - 20));
}

void udp_frag1024_sendto_test()
{
    int s;
    struct sockaddr_in my_addr;
    struct sockaddr_in to_addr;
    char buffer[4096];
    int sent;

    s = socket(AF_INET, SOCK_DGRAM, 0);

    my_addr.sin_family      = AF_INET;
    my_addr.sin_addr.s_addr = inet_addr("192.168.0.229");
    my_addr.sin_port        = htons(12345);

    bind(s,(struct sockaddr *)&my_addr,sizeof(my_addr));

    to_addr.sin_family      = AF_INET;
    to_addr.sin_addr.s_addr = inet_addr("192.168.0.19");
    to_addr.sin_port        = htons(12345);

    memset(buffer,'A',sizeof(buffer));

    sent = udp_frag1024_sendto(s,buffer,2047,0,(struct sockaddr *)&to_addr,sizeof(struct sockaddr_in));

    close(s);

    printf("sent %d bytes\n",sent);
}