void sockstats(PNATState pData) { char buff[256]; size_t n; struct socket *so, *so_next; lprint("\n"); lprint( "Proto[state] Sock Local Address, Port Remote Address, Port RecvQ SendQ\n"); QSOCKET_FOREACH(so, so_next, tcp) /* { */ n = RTStrPrintf(buff, sizeof(buff), "tcp[%s]", so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE"); while (n < 17) buff[n++] = ' '; buff[17] = 0; lprint("%s %3d %15s %5d ", buff, so->s, inet_ntoa(so->so_laddr), RT_N2H_U16(so->so_lport)); lprint("%15s %5d %5d %5d\n", inet_ntoa(so->so_faddr), RT_N2H_U16(so->so_fport), SBUF_LEN(&so->so_rcv), SBUF_LEN(&so->so_snd)); LOOP_LABEL(tcp, so, so_next); }
/** * Convert the given exported device structure from host to network byte order. * * @returns nothing. * @param pDevice The device structure to convert. */ DECLINLINE(void) usbProxyBackendUsbIpExportedDeviceN2H(PUsbIpExportedDevice pDevice) { pDevice->u32BusNum = RT_N2H_U32(pDevice->u32BusNum); pDevice->u32DevNum = RT_N2H_U32(pDevice->u32DevNum); pDevice->u32Speed = RT_N2H_U16(pDevice->u32Speed); pDevice->u16VendorId = RT_N2H_U16(pDevice->u16VendorId); pDevice->u16ProductId = RT_N2H_U16(pDevice->u16ProductId); pDevice->u16BcdDevice = RT_N2H_U16(pDevice->u16BcdDevice); }
static void tftpProcessACK(PNATState pData, PTFTPIPHDR pTftpIpHeader) { int rc; PTFTPSESSION pTftpSession = NULL; rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession); if (RT_FAILURE(rc)) return; AssertReturnVoid(tftpSendData(pData, pTftpSession, RT_N2H_U16(pTftpIpHeader->Core.u16TftpOpCode), pTftpIpHeader) == 0); }
static void tftp_handle_ack(PNATState pData, struct tftp_t *tp, int pktlen) { int s; s = tftp_session_find(pData, tp); if (s < 0) return; if (tftp_send_data(pData, &tftp_sessions[s], RT_N2H_U16(tp->x.tp_data.tp_block_nr) + 1, tp) < 0) { /* XXX */ } }
static void tftpProcessACK(PNATState pData, PTFTPIPHDR pTftpIpHeader) { int rc; PTFTPSESSION pTftpSession = NULL; rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession); if (RT_FAILURE(rc)) return; if (tftpSendData(pData, pTftpSession, RT_N2H_U16(pTftpIpHeader->Core.u16TftpOpCode), pTftpIpHeader)) LogRel(("NAT TFTP: failure\n")); }
void tftp_input(PNATState pData, struct mbuf *m) { struct tftp_t *tp = (struct tftp_t *)m->m_data; switch(RT_N2H_U16(tp->tp_op)) { case TFTP_RRQ: tftp_handle_rrq(pData, tp, m->m_len); break; case TFTP_ACK: tftp_handle_ack(pData, tp, m->m_len); break; } }
int slirpTftpInput(PNATState pData, struct mbuf *pMbuf) { PTFTPIPHDR pTftpIpHeader = NULL; AssertPtr(pData); AssertPtr(pMbuf); pTftpIpHeader = mtod(pMbuf, PTFTPIPHDR); switch(RT_N2H_U16(pTftpIpHeader->u16TftpOpType)) { case TFTP_RRQ: tftpProcessRRQ(pData, pTftpIpHeader, m_length(pMbuf, NULL)); break; case TFTP_ACK: tftpProcessACK(pData, pTftpIpHeader); break; default:; } LogFlowFuncLeaveRC(VINF_SUCCESS); return VINF_SUCCESS; }
/* * Ip input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level. */ void ip_input(PNATState pData, struct mbuf *m) { register struct ip *ip; int hlen = 0; int mlen = 0; STAM_PROFILE_START(&pData->StatIP_input, a); LogFlowFunc(("ENTER: m = %lx\n", (long)m)); ip = mtod(m, struct ip *); Log2(("ip_dst=%RTnaipv4(len:%d) m_len = %d\n", ip->ip_dst, RT_N2H_U16(ip->ip_len), m->m_len)); ipstat.ips_total++; { int rc; if (!(m->m_flags & M_SKIP_FIREWALL)) { STAM_PROFILE_START(&pData->StatALIAS_input, b); rc = LibAliasIn(select_alias(pData, m), mtod(m, char *), m_length(m, NULL)); STAM_PROFILE_STOP(&pData->StatALIAS_input, b); Log2(("NAT: LibAlias return %d\n", rc)); } else
/* 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; }
struct socket * udp_listen(PNATState pData, u_int32_t bind_addr, u_int port, u_int32_t laddr, u_int lport, int flags) { struct sockaddr_in addr; struct socket *so; socklen_t addrlen = sizeof(struct sockaddr_in); int opt = 1; LogFlowFunc(("ENTER: bind_addr:%RTnaipv4, port:%d, laddr:%RTnaipv4, lport:%d, flags:%x\n", bind_addr, RT_N2H_U16(port), laddr, RT_N2H_U16(lport), flags)); if ((so = socreate()) == NULL) { LogFlowFunc(("LEAVE: NULL\n")); return NULL; } so->s = socket(AF_INET, SOCK_DGRAM, 0); if (so->s == -1) { LogRel(("NAT: can't create datagram socket\n")); RTMemFree(so); LogFlowFunc(("LEAVE: NULL\n")); return NULL; } so->so_expire = curtime + SO_EXPIRE; so->so_type = IPPROTO_UDP; fd_nonblock(so->s); so->so_sottl = 0; so->so_sotos = 0; so->so_sodf = -1; SOCKET_LOCK_CREATE(so); QSOCKET_LOCK(udb); insque(pData, so, &udb); NSOCK_INC(); QSOCKET_UNLOCK(udb); memset(&addr, 0, sizeof(addr)); #ifdef RT_OS_DARWIN addr.sin_len = sizeof(addr); #endif addr.sin_family = AF_INET; addr.sin_addr.s_addr = bind_addr; addr.sin_port = port; if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) { LogRel(("NAT: udp bind to %RTnaipv4:%d failed, error %d\n", addr.sin_addr, RT_N2H_U16(port), errno)); udp_detach(pData, so); LogFlowFunc(("LEAVE: NULL\n")); return NULL; } setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR,(char *)&opt, sizeof(int)); /* setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE,(char *)&opt, sizeof(int)); */ getsockname(so->s,(struct sockaddr *)&addr,&addrlen); so->so_hladdr = addr.sin_addr; so->so_hlport = addr.sin_port; /* XXX: wtf are we setting so_faddr/so_fport here? */ so->so_fport = addr.sin_port; #if 0 /* The original check was completely broken, as the commented out * if statement was always true (INADDR_ANY=0). */ /** @todo vvl - alias_addr should be set (if required) * later by liabalias module. */ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) so->so_faddr = alias_addr; else #endif so->so_faddr = addr.sin_addr; so->so_lport = lport; so->so_laddr.s_addr = laddr; if (flags != SS_FACCEPTONCE) so->so_expire = 0; so->so_state = SS_ISFCONNECTED; LogFlowFunc(("LEAVE: %R[natsock]\n", so)); return so; }
DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader) { int rc = VINF_SUCCESS; char *pszTftpRRQRaw; size_t idxTftpRRQRaw = 0; int cbTftpRRQRaw = 0; int fWithArg = 0; int idxOptionArg = 0; AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER); AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER); AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER); LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader)); pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core; cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_OFFSETOF(TFTPIPHDR, Core); while(cbTftpRRQRaw) { idxTftpRRQRaw = RTStrNLen(pszTftpRRQRaw, 512 - idxTftpRRQRaw) + 1; if (RTStrNLen((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX) == 0) { rc = RTStrCopy((char *)pTftpSession->pszFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw); if (RT_FAILURE(rc)) { LogFlowFuncLeaveRC(rc); AssertRCReturn(rc,rc); } } else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE) { int idxFmt = 0; rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw); if (RT_FAILURE(rc)) { LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR); return VERR_INTERNAL_ERROR; } AssertReturn( g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR); pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType; } else if (fWithArg) { if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName)) { rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize); if (pTftpSession->OptionBlkSize.u64Value > UINT16_MAX) rc = VERR_INVALID_PARAMETER; } if ( RT_SUCCESS(rc) && !RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName)) rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTSize); /* @todo: we don't use timeout, but its value in the range 0-255 */ if ( RT_SUCCESS(rc) && !RTStrICmp("timeout", g_TftpDesc[idxOptionArg].pszName)) rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTimeout); /* @todo: unknown option detection */ if (RT_FAILURE(rc)) { LogFlowFuncLeaveRC(rc); AssertRCReturn(rc,rc); } fWithArg = 0; idxOptionArg = 0; } else { rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw); if (RT_SUCCESS(rc)) fWithArg = 1; else { LogFlowFuncLeaveRC(rc); AssertRCReturn(rc,rc); } } pszTftpRRQRaw += idxTftpRRQRaw; cbTftpRRQRaw -= idxTftpRRQRaw; } LogFlowFuncLeaveRC(rc); return rc; }
/** * Calculates the checksum of a pseudo header given an IPv6 header, ASSUMING * that there are no headers between the IPv6 header and the upper layer header. * * Use this method with create care! In most cases you should be using * RTNetIPv6PseudoChecksumEx. * * @returns 32-bit intermediary checksum value. * @param pIpHdr The IPv6 header (network endian (big)). */ RTDECL(uint32_t) RTNetIPv6PseudoChecksum(PCRTNETIPV6 pIpHdr) { return rtNetIPv6PseudoChecksumBits(&pIpHdr->ip6_src, &pIpHdr->ip6_dst, pIpHdr->ip6_nxt, RT_N2H_U16(pIpHdr->ip6_plen)); }
/** * Processes the data in the scratch buffer based on the current state. * * @returns VBox status code. */ int USBProxyBackendUsbIp::processData() { int rc = VINF_SUCCESS; switch (m->enmRecvState) { case kUsbIpRecvState_Hdr: { /* Check that the reply matches our expectations. */ if ( RT_N2H_U16(m->Scratch.RetDevList.u16Version) == USBIP_VERSION && RT_N2H_U16(m->Scratch.RetDevList.u16Cmd) == USBIP_REQ_RET_DEVLIST && RT_N2H_U32(m->Scratch.RetDevList.u32Status) == USBIP_STATUS_SUCCESS) { /* Populate the number of exported devices in the list and go to the next state. */ m->cDevicesLeft = RT_N2H_U32(m->Scratch.RetDevList.u32DevicesExported); if (m->cDevicesLeft) advanceState(kUsbIpRecvState_ExportedDevice); else advanceState(kUsbIpRecvState_None); } else { LogRelMax(10, ("USB/IP: Host sent an invalid reply to the list exported device request (Version: %#x Cmd: %#x Status: %#x)\n", RT_N2H_U16(m->Scratch.RetDevList.u16Version), RT_N2H_U16(m->Scratch.RetDevList.u16Cmd), RT_N2H_U32(m->Scratch.RetDevList.u32Status))); rc = VERR_INVALID_STATE; } break; } case kUsbIpRecvState_ExportedDevice: { /* Create a new device and add it to the list. */ usbProxyBackendUsbIpExportedDeviceN2H(&m->Scratch.ExportedDevice); rc = addDeviceToList(&m->Scratch.ExportedDevice); if (RT_SUCCESS(rc)) { m->cInterfacesLeft = m->Scratch.ExportedDevice.bNumInterfaces; if (m->cInterfacesLeft) advanceState(kUsbIpRecvState_DeviceInterface); else { m->cDevicesLeft--; if (m->cDevicesLeft) advanceState(kUsbIpRecvState_ExportedDevice); else advanceState(kUsbIpRecvState_None); } } break; } case kUsbIpRecvState_DeviceInterface: { /* * If all interfaces for the current device were received receive the next device * if there is another one left, if not we are done with the current request. */ m->cInterfacesLeft--; if (m->cInterfacesLeft) advanceState(kUsbIpRecvState_DeviceInterface); else { m->cDevicesLeft--; if (m->cDevicesLeft) advanceState(kUsbIpRecvState_ExportedDevice); else advanceState(kUsbIpRecvState_None); } break; } case kUsbIpRecvState_None: default: AssertMsgFailed(("Invalid USB/IP receive state %d\n", m->enmRecvState)); return VERR_INVALID_STATE; } return rc; }
/* 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; }
/* * Ip input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level. */ void ip_input(PNATState pData, struct mbuf *m) { register struct ip *ip; int hlen = 0; int mlen = 0; STAM_PROFILE_START(&pData->StatIP_input, a); LogFlowFunc(("ENTER: m = %lx\n", (long)m)); ip = mtod(m, struct ip *); Log2(("ip_dst=%RTnaipv4(len:%d) m_len = %d\n", ip->ip_dst, RT_N2H_U16(ip->ip_len), m->m_len)); ipstat.ips_total++; { int rc; STAM_PROFILE_START(&pData->StatALIAS_input, b); rc = LibAliasIn(select_alias(pData, m), mtod(m, char *), m_length(m, NULL)); STAM_PROFILE_STOP(&pData->StatALIAS_input, b); Log2(("NAT: LibAlias return %d\n", rc)); if (m->m_len != RT_N2H_U16(ip->ip_len)) m->m_len = RT_N2H_U16(ip->ip_len); } mlen = m->m_len; if (mlen < sizeof(struct ip)) { ipstat.ips_toosmall++; goto bad_free_m; } ip = mtod(m, struct ip *); if (ip->ip_v != IPVERSION) { ipstat.ips_badvers++; goto bad_free_m; } hlen = ip->ip_hl << 2; if ( hlen < sizeof(struct ip) || hlen > m->m_len) { /* min header length */ ipstat.ips_badhlen++; /* or packet too short */ goto bad_free_m; } /* keep ip header intact for ICMP reply * ip->ip_sum = cksum(m, hlen); * if (ip->ip_sum) { */ if (cksum(m, hlen)) { ipstat.ips_badsum++; goto bad_free_m; } /* * Convert fields to host representation. */ NTOHS(ip->ip_len); if (ip->ip_len < hlen) { ipstat.ips_badlen++; goto bad_free_m; } NTOHS(ip->ip_id); NTOHS(ip->ip_off); /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ if (mlen < ip->ip_len) { ipstat.ips_tooshort++; goto bad_free_m; } /* Should drop packet if mbuf too long? hmmm... */ if (mlen > ip->ip_len) m_adj(m, ip->ip_len - m->m_len); /* check ip_ttl for a correct ICMP reply */ if (ip->ip_ttl==0 || ip->ip_ttl == 1) { icmp_error(pData, m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl"); goto no_free_m; } ip->ip_ttl--; /* * If offset or IP_MF are set, must reassemble. * Otherwise, nothing need be done. * (We could look in the reassembly queue to see * if the packet was previously fragmented, * but it's not worth the time; just let them time out.) * */ if (ip->ip_off & (IP_MF | IP_OFFMASK)) { m = ip_reass(pData, m); if (m == NULL) goto no_free_m; ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; }