static void udp6_send(netmsg_t msg) { struct socket *so = msg->send.base.nm_so; struct mbuf *m = msg->send.nm_m; struct sockaddr *addr = msg->send.nm_addr; struct mbuf *control = msg->send.nm_control; struct thread *td = msg->send.nm_td; struct inpcb *inp; int error = 0; inp = so->so_pcb; if (inp == NULL) { error = EINVAL; goto bad; } if (addr) { struct sockaddr_in6 *sin6; if (addr->sa_len != sizeof(struct sockaddr_in6)) { error = EINVAL; goto bad; } if (addr->sa_family != AF_INET6) { error = EAFNOSUPPORT; goto bad; } sin6 = (struct sockaddr_in6 *)addr; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { error = EADDRNOTAVAIL; goto bad; } } error = udp6_output(inp, m, addr, control, td); lwkt_replymsg(&msg->send.base.lmsg, error); return; bad: m_freem(m); lwkt_replymsg(&msg->send.base.lmsg, error); }
static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m, struct tftp_t *recv_tp) { if (spt->client_addr.ss_family == AF_INET6) { struct sockaddr_in6 sa6, da6; sa6.sin6_addr = spt->slirp->vhost_addr6; sa6.sin6_port = recv_tp->udp.uh_dport; da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr; da6.sin6_port = spt->client_port; udp6_output(NULL, m, &sa6, &da6); } else { struct sockaddr_in sa4, da4; sa4.sin_addr = spt->slirp->vhost_addr; sa4.sin_port = recv_tp->udp.uh_dport; da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr; da4.sin_port = spt->client_port; udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY); } }
static int udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct thread *td) { struct inpcb *inp; struct inpcbinfo *pcbinfo; int error = 0; pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol); inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp6_send: inp == NULL")); INP_WLOCK(inp); if (addr) { if (addr->sa_len != sizeof(struct sockaddr_in6)) { error = EINVAL; goto bad; } if (addr->sa_family != AF_INET6) { error = EAFNOSUPPORT; goto bad; } } #ifdef INET if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { int hasv4addr; struct sockaddr_in6 *sin6 = NULL; if (addr == NULL) hasv4addr = (inp->inp_vflag & INP_IPV4); else { sin6 = (struct sockaddr_in6 *)addr; hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ? 1 : 0; } if (hasv4addr) { struct pr_usrreqs *pru; uint8_t nxt; nxt = (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDP) ? IPPROTO_UDP : IPPROTO_UDPLITE; /* * XXXRW: We release UDP-layer locks before calling * udp_send() in order to avoid recursion. However, * this does mean there is a short window where inp's * fields are unstable. Could this lead to a * potential race in which the factors causing us to * select the UDPv4 output routine are invalidated? */ INP_WUNLOCK(inp); if (sin6) in6_sin6_2_sin_in_sock(addr); pru = inetsw[ip_protox[nxt]].pr_usrreqs; /* addr will just be freed in sendit(). */ return ((*pru->pru_send)(so, flags, m, addr, control, td)); } } #endif #ifdef MAC mac_inpcb_create_mbuf(inp, m); #endif INP_HASH_WLOCK(pcbinfo); error = udp6_output(inp, m, addr, control, td); INP_HASH_WUNLOCK(pcbinfo); INP_WUNLOCK(inp); return (error); bad: INP_WUNLOCK(inp); m_freem(m); return (error); }
/* * recvfrom() a UDP socket */ void sorecvfrom(struct socket *so) { struct sockaddr_storage addr; struct sockaddr_storage saddr, daddr; socklen_t addrlen = sizeof(struct sockaddr_storage); DEBUG_CALL("sorecvfrom"); DEBUG_ARG("so = %p", so); if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */ char buff[256]; int len; len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen); /* XXX Check if reply is "correct"? */ if(len == -1 || len == 0) { u_char code=ICMP_UNREACH_PORT; if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET; DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n", errno,strerror(errno))); icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno)); } else { icmp_reflect(so->so_m); so->so_m = NULL; /* Don't m_free() it again! */ } /* No need for this socket anymore, udp_detach it */ udp_detach(so); } else { /* A "normal" UDP packet */ struct mbuf *m; int len; #ifdef _WIN32 unsigned long n; #else int n; #endif m = m_get(so->slirp); if (!m) { return; } switch (so->so_ffamily) { case AF_INET: m->m_data += IF_MAXLINKHDR + sizeof(struct udpiphdr); break; case AF_INET6: m->m_data += IF_MAXLINKHDR + sizeof(struct ip6) + sizeof(struct udphdr); break; default: g_assert_not_reached(); break; } /* * XXX Shouldn't FIONREAD packets destined for port 53, * but I don't know the max packet size for DNS lookups */ len = M_FREEROOM(m); /* if (so->so_fport != htons(53)) { */ ioctlsocket(so->s, FIONREAD, &n); if (n > len) { n = (m->m_data - m->m_dat) + m->m_len + n + 1; m_inc(m, n); len = M_FREEROOM(m); } /* } */ m->m_len = recvfrom(so->s, m->m_data, len, 0, (struct sockaddr *)&addr, &addrlen); DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n", m->m_len, errno,strerror(errno))); if(m->m_len<0) { /* Report error as ICMP */ switch (so->so_lfamily) { uint8_t code; case AF_INET: code = ICMP_UNREACH_PORT; if (errno == EHOSTUNREACH) { code = ICMP_UNREACH_HOST; } else if (errno == ENETUNREACH) { code = ICMP_UNREACH_NET; } DEBUG_MISC((dfd, " rx error, tx icmp ICMP_UNREACH:%i\n", code)); icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno)); break; case AF_INET6: code = ICMP6_UNREACH_PORT; if (errno == EHOSTUNREACH) { code = ICMP6_UNREACH_ADDRESS; } else if (errno == ENETUNREACH) { code = ICMP6_UNREACH_NO_ROUTE; } DEBUG_MISC((dfd, " rx error, tx icmp6 ICMP_UNREACH:%i\n", code)); icmp6_send_error(so->so_m, ICMP6_UNREACH, code); break; default: g_assert_not_reached(); break; } m_free(m); } else { /* * Hack: domain name lookup will be used the most for UDP, * and since they'll only be used once there's no need * for the 4 minute (or whatever) timeout... So we time them * out much quicker (10 seconds for now...) */ if (so->so_expire) { if (so->so_fport == htons(53)) so->so_expire = curtime + SO_EXPIREFAST; else so->so_expire = curtime + SO_EXPIRE; } /* * If this packet was destined for CTL_ADDR, * make it look like that's where it came from */ saddr = addr; sotranslate_in(so, &saddr); daddr = so->lhost.ss; switch (so->so_ffamily) { case AF_INET: udp_output(so, m, (struct sockaddr_in *) &saddr, (struct sockaddr_in *) &daddr, so->so_iptos); break; case AF_INET6: udp6_output(so, m, (struct sockaddr_in6 *) &saddr, (struct sockaddr_in6 *) &daddr); break; default: g_assert_not_reached(); break; } } /* rx error */ } /* if ping packet */ }