+/** + * udp_send_conn_fail_icmp4() - Construct and send ICMPv4 to local peer + * @c: Execution context + * @ee: Extended error descriptor + * @ref: epoll reference + * @in: First bytes (max 8) of original UDP message body + * @dlen: Length of the read part of original UDP message body + */ +static void udp_send_conn_fail_icmp4(const struct ctx *c, + const struct sock_extended_err *ee, + const struct flowside *toside, + void *in, size_t dlen) +{ + struct in_addr oaddr = toside->oaddr.v4mapped.a4; + struct in_addr eaddr = toside->eaddr.v4mapped.a4; + in_port_t eport = toside->eport; + in_port_t oport = toside->oport; + struct { + struct icmphdr icmp4h; + struct iphdr ip4h; + struct udphdr uh; + char data[ICMP4_MAX_DLEN]; + } __attribute__((packed, aligned(__alignof__(max_align_t)))) msg; + size_t msglen = sizeof(msg) - sizeof(msg.data) + dlen; + + ASSERT(dlen <= ICMP4_MAX_DLEN); + memset(&msg, 0, sizeof(msg)); + msg.icmp4h.type = ee->ee_type; + msg.icmp4h.code = ee->ee_code; + if (ee->ee_type == ICMP_DEST_UNREACH && ee->ee_code == ICMP_FRAG_NEEDED) + msg.icmp4h.un.frag.mtu = htons((uint16_t) ee->ee_info); + + /* Reconstruct the original headers as returned in the ICMP message */ + tap_push_ip4h(&msg.ip4h, eaddr, oaddr, dlen, IPPROTO_UDP); + tap_push_uh4(&msg.uh, eaddr, eport, oaddr, oport, in, dlen); + memcpy(&msg.data, in, dlen); + + tap_icmp4_send(c, oaddr, eaddr, &msg, msglen); +}The destination IP of the origin packet might not be the source IP of an ICMP error message, if a router sent this ICMP error message. Increase local MTU and try this program: ``` #packet-too-big.py #ip link set eth0 mtu 1520 from socket import * import time IP_RECVERR=0xb IP_MTU_DISCOVER=0xa IP_PMTUDISC_PROBE=0x3 with socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP) as sock: sock.setsockopt(IPPROTO_IP,IP_RECVERR,1) sock.setsockopt(IPPROTO_IP,IP_MTU_DISCOVER,IP_PMTUDISC_PROBE) bigPacket=bytes(1480) sock.sendto(bigPacket,("151.101.1.6",443)) time.sleep(0.1) print(sock.recvmsg(1480,1024,MSG_ERRQUEUE)) ```if (ref.type == EPOLL_TYPE_UDP_REPLY) { flow_sidx_t sidx = flow_sidx_opposite(ref.flowside); const struct flowside *toside = flowside_at_sidx(sidx); - - udp_send_conn_fail_icmp4(c, ee, toside, data, rc); + size_t dlen = rc; + + if (hdr->cmsg_level == IPPROTO_IP) { + dlen = MIN(dlen, ICMP4_MAX_DLEN); + udp_send_conn_fail_icmp4(c, ee, toside, data, dlen); + } else if (hdr->cmsg_level == IPPROTO_IPV6) { + udp_send_conn_fail_icmp6(c, ee, toside, data, + dlen, sidx.flowi); + } } else { trace("Ignoring received IP_RECVERR cmsg on listener socket"); }If the socket is dual-stack, cmsg_level may not match cmsg_data. ``` #dual-stack-test.py from socket import * import time IP_RECVERR=0xb with socket(AF_INET6,SOCK_DGRAM,IPPROTO_UDP) as sock: sock.setsockopt(IPPROTO_IP,IP_RECVERR,1) sock.setsockopt(IPPROTO_IP,IP_TTL,1) packet=bytes(8) sock.sendto(packet,("::ffff:151.101.1.6",443)) time.sleep(0.1) print(sock.recvmsg(1472,1024,MSG_ERRQUEUE)) ```