问题
Can UDP packet be fragmented to several smaller ones if it exceeds MTU? It seems that MTU fragmentation is about IP layer so I think it can.
If so, what is the recommended max. packet size to send over UDP to avoid fragmentation and why?
回答1:
Any IP datagram can be fragmented if it is larger than the MTU. Whether it contains UDP, TCP, ICMP, etc. does not matter.
Most Ethernet networks support a 1500 byte MTU. With the IPv4 header being 20 bytes and the UDP header being 8 bytes, the payload of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation.
This is assuming no IP options exist in the packet. If so, the payload will need to be smaller than that to account for it.
This applies to IPv4 only. IPv6 does not support fragmentation.
You can see also this question regarding MTU for UDP.
回答2:
If you want packet fragmentation at specific size, the following example would be helpful.
#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);
}
来源:https://stackoverflow.com/questions/38723393/can-udp-packet-be-fragmented-to-several-smaller-ones