/* 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; }
void udp_input(register struct mbuf *m, int iphlen) { register struct ip *ip; register struct udphdr *uh; /* struct mbuf *opts = 0;*/ int len; struct ip save_ip; struct socket *so; DEBUG_CALL("udp_input"); DEBUG_ARG("m = %lx", (long)m); DEBUG_ARG("iphlen = %d", iphlen); STAT(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 = ntohs((u_int16_t)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len) { STAT(udpstat.udps_badlen++); goto bad; } 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_mbuf, 0, sizeof(struct mbuf_ptr)); ((struct ipovly *)ip)->ih_x1 = 0; ((struct ipovly *)ip)->ih_len = uh->uh_ulen; /* keep uh_sum for ICMP reply * uh->uh_sum = cksum(m, len + sizeof (struct ip)); * if (uh->uh_sum) { */ if(cksum(m, len + sizeof(struct ip))) { STAT(udpstat.udps_badsum++); goto bad; } } /* * handle DHCP/BOOTP */ if (ntohs(uh->uh_dport) == BOOTP_SERVER) { bootp_input(m); goto bad; } if (slirp_restrict) goto bad; /* * handle TFTP */ if (ntohs(uh->uh_dport) == TFTP_SERVER) { tftp_input(m); goto bad; } /* * 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) { tmp->so_faddr.s_addr = ip->ip_dst.s_addr; tmp->so_fport = uh->uh_dport; so = tmp; break; } } if (tmp == &udb) { so = NULL; } else { STAT(udpstat.udpps_pcbcachemiss++); udp_last_so = so; } } if (so == NULL) { /* * If there's no socket for this packet, * create one */ if ((so = socreate()) == NULL) goto bad; if(udp_attach(so) == -1) { DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", errno,strerror(errno))); sofree(so); goto bad; } /* * Setup fields */ /* udp_last_so = so; */ so->so_laddr = ip->ip_src; so->so_lport = uh->uh_sport; if ((so->so_iptos = udp_tos(so)) == 0) so->so_iptos = ip->ip_tos; /* * 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 */ iphlen += sizeof(struct udphdr); m->m_len -= iphlen; m->m_data += iphlen; /* * Now we sendto() the packet. */ if (so->so_emu) udp_emu(so, m); if(sosendto(so,m) == -1) { m->m_len += iphlen; m->m_data -= iphlen; *ip=save_ip; DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); } m_free(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 */ return; bad: m_freem(m); /* if (opts) m_freem(opts); */ return; }
/* 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; 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 == len)); Assert((ip->ip_len + iphlen == 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)); } 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, RT_H2N_U16_C(uh->uh_sport), ip->ip_dst, RT_H2N_U16_C(uh->uh_dport))); 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; slirpMbufTagService(pData, m, CTL_DNS); /* udp_output2() expects a pointer to the body of UDP packet. */ m->m_data += sizeof(struct udpiphdr); m->m_len -= sizeof(struct udpiphdr); 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; } /* * 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; } } 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; } 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; } /* * Setup fields */ /* udp_last_so = so; */ so->so_laddr = ip->ip_src; so->so_lport = uh->uh_sport; so->so_iptos = ip->ip_tos; /* * 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; ret = setsockopt(so->s, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof(ttl)); if (ret < 0) LogRel(("NAT: Error (%s) occurred while setting TTL(%d) attribute " "of IP packet to socket %R[natsock]\n", strerror(errno), ip->ip_ttl, so)); 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)); #if 0 /* ICMP_SOURCEQUENCH haven't got any effect, the idea here * inform guest about the exosting NAT resources with assumption that * that guest reduce traffic. But it doesn't work */ if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == ENOTCONN) icmp_error(pData, m, ICMP_SOURCEQUENCH, 0, 1, strerror(errno)); else #endif 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. */ m_freem(pData, m); LogFlowFuncLeave(); return; }