/* m->m_data points at ip packet header * m->m_len length ip packet * ip->ip_len length data (IPDU) */ void udp_input(PNATState pData, register struct mbuf *m, int iphlen) { register struct ip *ip; register struct udphdr *uh; int len; struct ip save_ip; struct socket *so; int ret; int ttl, tos; LogFlowFunc(("ENTER: m = %p, iphlen = %d\n", m, iphlen)); ip = mtod(m, struct ip *); Log2(("%RTnaipv4 iphlen = %d\n", ip->ip_dst, iphlen)); udpstat.udps_ipackets++; /* * Strip IP options, if any; should skip this, * make available to user, and use on returned packets, * but we don't yet have a way to check the checksum * with options still present. */ if (iphlen > sizeof(struct ip)) { ip_stripoptions(m, (struct mbuf *)0); iphlen = sizeof(struct ip); } /* * Get IP and UDP header together in first mbuf. */ ip = mtod(m, struct ip *); uh = (struct udphdr *)((caddr_t)ip + iphlen); /* * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ len = RT_N2H_U16((u_int16_t)uh->uh_ulen); Assert(ip->ip_len + iphlen == (ssize_t)m_length(m, NULL)); if (ip->ip_len != len) { if (len > ip->ip_len) { udpstat.udps_badlen++; Log3(("NAT: IP(id: %hd) has bad size\n", ip->ip_id)); goto bad_free_mbuf; } m_adj(m, len - ip->ip_len); ip->ip_len = len; } /* * Save a copy of the IP header in case we want restore it * for sending an ICMP error message in response. */ save_ip = *ip; save_ip.ip_len+= iphlen; /* tcp_input subtracts this */ /* * Checksum extended UDP header and data. */ if (udpcksum && uh->uh_sum) { memset(((struct ipovly *)ip)->ih_x1, 0, 9); ((struct ipovly *)ip)->ih_len = uh->uh_ulen; #if 0 /* keep uh_sum for ICMP reply */ uh->uh_sum = cksum(m, len + sizeof (struct ip)); if (uh->uh_sum) { #endif if (cksum(m, len + iphlen)) { udpstat.udps_badsum++; Log3(("NAT: IP(id: %hd) has bad (udp) cksum\n", ip->ip_id)); goto bad_free_mbuf; } } #if 0 } #endif /* * handle DHCP/BOOTP */ if (uh->uh_dport == RT_H2N_U16_C(BOOTP_SERVER)) { bootp_input(pData, m); goto done_free_mbuf; } LogFunc(("uh src: %RTnaipv4:%d, dst: %RTnaipv4:%d\n", ip->ip_src.s_addr, RT_N2H_U16(uh->uh_sport), ip->ip_dst.s_addr, RT_N2H_U16(uh->uh_dport))); /* * handle DNS host resolver without creating a socket */ if ( pData->fUseHostResolver && uh->uh_dport == RT_H2N_U16_C(53) && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS)) { struct sockaddr_in dst, src; src.sin_addr.s_addr = ip->ip_dst.s_addr; src.sin_port = uh->uh_dport; dst.sin_addr.s_addr = ip->ip_src.s_addr; dst.sin_port = uh->uh_sport; m_adj(m, sizeof(struct udpiphdr)); m = hostresolver(pData, m, ip->ip_src.s_addr, uh->uh_sport); if (m == NULL) goto done_free_mbuf; slirpMbufTagService(pData, m, CTL_DNS); udp_output2(pData, NULL, m, &src, &dst, IPTOS_LOWDELAY); LogFlowFuncLeave(); return; } /* * handle TFTP */ if ( uh->uh_dport == RT_H2N_U16_C(TFTP_SERVER) && CTL_CHECK(ip->ip_dst.s_addr, CTL_TFTP)) { if (pData->pvTftpSessions) slirpTftpInput(pData, m); goto done_free_mbuf; } /* * XXX: DNS proxy currently relies on the fact that each socket * only serves one request. */ if ( pData->fUseDnsProxy && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS) && (uh->uh_dport == RT_H2N_U16_C(53))) { so = NULL; goto new_socket; } /* * Locate pcb for datagram. */ so = udp_last_so; if ( so->so_lport != uh->uh_sport || so->so_laddr.s_addr != ip->ip_src.s_addr) { struct socket *tmp; for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next) { if ( tmp->so_lport == uh->uh_sport && tmp->so_laddr.s_addr == ip->ip_src.s_addr) { so = tmp; break; } } if (tmp == &udb) so = NULL; else { udpstat.udpps_pcbcachemiss++; udp_last_so = so; } } new_socket: if (so == NULL) { /* * If there's no socket for this packet, * create one */ if ((so = socreate()) == NULL) { Log2(("NAT: IP(id: %hd) failed to create socket\n", ip->ip_id)); goto bad_free_mbuf; } /* * Setup fields */ so->so_laddr = ip->ip_src; so->so_lport = uh->uh_sport; so->so_iptos = ip->ip_tos; if (udp_attach(pData, so) <= 0) { Log2(("NAT: IP(id: %hd) udp_attach errno = %d (%s)\n", ip->ip_id, errno, strerror(errno))); sofree(pData, so); goto bad_free_mbuf; } /* udp_last_so = so; */ /* * XXXXX Here, check if it's in udpexec_list, * and if it is, do the fork_exec() etc. */ } so->so_faddr = ip->ip_dst; /* XXX */ so->so_fport = uh->uh_dport; /* XXX */ Assert(so->so_type == IPPROTO_UDP); /* * DNS proxy */ if ( pData->fUseDnsProxy && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS) && (uh->uh_dport == RT_H2N_U16_C(53))) { dnsproxy_query(pData, so, m, iphlen); goto done_free_mbuf; } iphlen += sizeof(struct udphdr); m->m_len -= iphlen; m->m_data += iphlen; ttl = ip->ip_ttl = save_ip.ip_ttl; if (ttl != so->so_sottl) { ret = setsockopt(so->s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); if (RT_LIKELY(ret == 0)) so->so_sottl = ttl; } tos = save_ip.ip_tos; if (tos != so->so_sotos) { ret = setsockopt(so->s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)); if (RT_LIKELY(ret == 0)) so->so_sotos = tos; } { /* * Different OSes have different socket options for DF. We * can't use IP_HDRINCL here as it's only valid for SOCK_RAW. */ # define USE_DF_OPTION(_Optname) \ const int dfopt = _Optname #if defined(IP_MTU_DISCOVER) USE_DF_OPTION(IP_MTU_DISCOVER); #elif defined(IP_DONTFRAG) /* Solaris 11+, FreeBSD */ USE_DF_OPTION(IP_DONTFRAG); #elif defined(IP_DONTFRAGMENT) /* Windows */ USE_DF_OPTION(IP_DONTFRAGMENT); #else USE_DF_OPTION(0); #endif if (dfopt) { int df = (save_ip.ip_off & IP_DF) != 0; #if defined(IP_MTU_DISCOVER) df = df ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; #endif if (df != so->so_sodf) { ret = setsockopt(so->s, IPPROTO_IP, dfopt, (char *)&df, sizeof(df)); if (RT_LIKELY(ret == 0)) so->so_sodf = df; } } } if ( sosendto(pData, so, m) == -1 && ( !soIgnorableErrorCode(errno) && errno != ENOTCONN)) { m->m_len += iphlen; m->m_data -= iphlen; *ip = save_ip; Log2(("NAT: UDP tx errno = %d (%s) on sent to %RTnaipv4\n", errno, strerror(errno), ip->ip_dst)); icmp_error(pData, m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); so->so_m = NULL; LogFlowFuncLeave(); return; } if (so->so_m) m_freem(pData, so->so_m); /* used for ICMP if error on sorecvfrom */ /* restore the orig mbuf packet */ m->m_len += iphlen; m->m_data -= iphlen; *ip = save_ip; so->so_m = m; /* ICMP backup */ LogFlowFuncLeave(); return; bad_free_mbuf: Log2(("NAT: UDP(id: %hd) datagram to %RTnaipv4 with size(%d) claimed as bad\n", ip->ip_id, &ip->ip_dst, ip->ip_len)); done_free_mbuf: /* some services like bootp(built-in), dns(buildt-in) and dhcp don't need sockets * and create new m'buffers to send them to guest, so we'll free their incomming * buffers here. */ if (m != NULL) m_freem(pData, m); LogFlowFuncLeave(); return; }
static void pxudp_pcb_forward_outbound(struct pxudp *pxudp, struct pbuf *p, ip_addr_t *addr, u16_t port) { int status; LWIP_UNUSED_ARG(addr); LWIP_UNUSED_ARG(port); if (!pxudp->is_mapped && pxudp_ttl_expired(p)) { return; } if (!ip_current_is_v6()) { /* IPv4 */ const struct ip_hdr *iph = ip_current_header(); int ttl, tos, df; /* * Different OSes have different socket options for DF. * Unlike pxping.c, we can't use IP_HDRINCL here as it's only * valid for SOCK_RAW. */ # define USE_DF_OPTION(_Optname) \ const int dfopt = _Optname; \ const char * const dfoptname = #_Optname; #if defined(IP_MTU_DISCOVER) /* Linux */ USE_DF_OPTION(IP_MTU_DISCOVER); #elif defined(IP_DONTFRAG) /* Solaris 11+, FreeBSD */ USE_DF_OPTION(IP_DONTFRAG); #elif defined(IP_DONTFRAGMENT) /* Windows */ USE_DF_OPTION(IP_DONTFRAGMENT); #else USE_DF_OPTION(0); #endif ttl = IPH_TTL(iph); if (!pxudp->is_mapped) { LWIP_ASSERT1(ttl > 1); --ttl; } if (ttl != pxudp->ttl) { status = setsockopt(pxudp->sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); if (RT_LIKELY(status == 0)) { pxudp->ttl = ttl; } else { DPRINTF(("IP_TTL: %R[sockerr]\n", SOCKERRNO())); } } tos = IPH_TOS(iph); if (tos != pxudp->tos) { status = setsockopt(pxudp->sock, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)); if (RT_LIKELY(status == 0)) { pxudp->tos = tos; } else { DPRINTF(("IP_TOS: %R[sockerr]\n", SOCKERRNO())); } } if (dfopt) { df = (IPH_OFFSET(iph) & PP_HTONS(IP_DF)) != 0; #if defined(IP_MTU_DISCOVER) df = df ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; #endif if (df != pxudp->df) { status = setsockopt(pxudp->sock, IPPROTO_IP, dfopt, (char *)&df, sizeof(df)); if (RT_LIKELY(status == 0)) { pxudp->df = df; } else { DPRINTF(("%s: %R[sockerr]\n", dfoptname, SOCKERRNO())); } } } } else { /* IPv6 */ const struct ip6_hdr *iph = ip6_current_header(); int ttl; ttl = IP6H_HOPLIM(iph); if (!pxudp->is_mapped) { LWIP_ASSERT1(ttl > 1); --ttl; } if (ttl != pxudp->ttl) { status = setsockopt(pxudp->sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&ttl, sizeof(ttl)); if (RT_LIKELY(status == 0)) { pxudp->ttl = ttl; } else { DPRINTF(("IPV6_UNICAST_HOPS: %R[sockerr]\n", SOCKERRNO())); } } } if (pxudp->pcb->local_port == 53) { ++pxudp->count; } proxy_sendto(pxudp->sock, p, NULL, 0); pbuf_free(p); }
$NetBSD$ --- src/VBox/NetworkServices/NAT/pxudp.c.orig 2016-03-04 19:29:43.000000000 +0000 +++ src/VBox/NetworkServices/NAT/pxudp.c @@ -603,7 +603,7 @@ pxudp_pcb_forward_outbound(struct pxudp const char * const dfoptname = #_Optname; #if defined(IP_MTU_DISCOVER) /* Linux */ USE_DF_OPTION(IP_MTU_DISCOVER); -#elif defined(IP_DONTFRAG) /* Solaris 11+, FreeBSD */ +#elif defined(IP_DONTFRAG) /* Solaris 11+, FreeBSD, NetBSD */ USE_DF_OPTION(IP_DONTFRAG); #elif defined(IP_DONTFRAGMENT) /* Windows */ USE_DF_OPTION(IP_DONTFRAGMENT);