X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=slirp%2Fudp.c;h=b105f871f377a02074c6436a6f62bef5cedc4d32;hb=c9fea5d701f8fd33f0843728ec264d95cee3ed37;hp=2504d119a48dcf5ad35e2bd8b8bbeaa31c32d6f6;hpb=5fafdf24ef2c090c164d4dc89684b3f379dbdd87;p=qemu.git diff --git a/slirp/udp.c b/slirp/udp.c index 2504d119a..b105f871f 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -10,11 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -45,39 +41,32 @@ #include #include "ip_icmp.h" -struct udpstat udpstat; - -struct socket udb; - -/* - * UDP protocol implementation. - * Per RFC 768, August, 1980. - */ -#ifndef COMPAT_42 -int udpcksum = 1; -#else -int udpcksum = 0; /* XXX */ -#endif - -struct socket *udp_last_so = &udb; +static uint8_t udp_tos(struct socket *so); void -udp_init() +udp_init(Slirp *slirp) { - udb.so_next = udb.so_prev = &udb; + slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb; + slirp->udp_last_so = &slirp->udb; } + +void udp_cleanup(Slirp *slirp) +{ + while (slirp->udb.so_next != &slirp->udb) { + udp_detach(slirp->udb.so_next); + } +} + /* m->m_data points at ip packet header * m->m_len length ip packet * ip->ip_len length data (IPDU) */ void -udp_input(m, iphlen) - register struct mbuf *m; - int iphlen; +udp_input(register struct mbuf *m, int iphlen) { + Slirp *slirp = m->slirp; register struct ip *ip; register struct udphdr *uh; -/* struct mbuf *opts = 0;*/ int len; struct ip save_ip; struct socket *so; @@ -86,8 +75,6 @@ udp_input(m, iphlen) DEBUG_ARG("m = %lx", (long)m); DEBUG_ARG("iphlen = %d", iphlen); - udpstat.udps_ipackets++; - /* * Strip IP options, if any; should skip this, * make available to user, and use on returned packets, @@ -109,11 +96,10 @@ udp_input(m, 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); + len = ntohs((uint16_t)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len) { - udpstat.udps_badlen++; goto bad; } m_adj(m, len - ip->ip_len); @@ -130,17 +116,11 @@ udp_input(m, iphlen) /* * Checksum extended UDP header and data. */ - if (udpcksum && uh->uh_sum) { - ((struct ipovly *)ip)->ih_next = 0; - ((struct ipovly *)ip)->ih_prev = 0; + if (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))) { - udpstat.udps_badsum++; goto bad; } } @@ -148,41 +128,46 @@ udp_input(m, iphlen) /* * handle DHCP/BOOTP */ - if (ntohs(uh->uh_dport) == BOOTP_SERVER) { - bootp_input(m); - goto bad; - } + if (ntohs(uh->uh_dport) == BOOTP_SERVER && + (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || + ip->ip_dst.s_addr == 0xffffffff)) { + bootp_input(m); + goto bad; + } /* * handle TFTP */ - if (ntohs(uh->uh_dport) == TFTP_SERVER) { + if (ntohs(uh->uh_dport) == TFTP_SERVER && + ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { tftp_input(m); goto bad; } + if (slirp->restricted) { + goto bad; + } + /* * Locate pcb for datagram. */ - so = udp_last_so; + so = slirp->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) { + + for (tmp = slirp->udb.so_next; tmp != &slirp->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) { + if (tmp == &slirp->udb) { so = NULL; } else { - udpstat.udpps_pcbcachemiss++; - udp_last_so = so; + slirp->udp_last_so = so; } } @@ -191,24 +176,26 @@ udp_input(m, iphlen) * If there's no socket for this packet, * create one */ - if ((so = socreate()) == NULL) goto bad; + so = socreate(slirp); + if (!so) { + 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. @@ -225,15 +212,12 @@ udp_input(m, 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)); + icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); } m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ @@ -246,9 +230,7 @@ udp_input(m, iphlen) return; bad: - m_freem(m); - /* if (opts) m_freem(opts); */ - return; + m_free(m); } int udp_output2(struct socket *so, struct mbuf *m, @@ -275,10 +257,10 @@ int udp_output2(struct socket *so, struct mbuf *m, * and addresses and length put into network format. */ ui = mtod(m, struct udpiphdr *); - ui->ui_next = ui->ui_prev = 0; + memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr)); ui->ui_x1 = 0; ui->ui_pr = IPPROTO_UDP; - ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* + sizeof (struct udphdr)); */ + ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* XXXXX Check for from-one-location sockets, or from-any-location sockets */ ui->ui_src = saddr->sin_addr; ui->ui_dst = daddr->sin_addr; @@ -290,17 +272,13 @@ int udp_output2(struct socket *so, struct mbuf *m, * Stuff checksum and output datagram. */ ui->ui_sum = 0; - if (udpcksum) { - if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ m->m_len)) == 0) + if ((ui->ui_sum = cksum(m, m->m_len)) == 0) ui->ui_sum = 0xffff; - } ((struct ip *)ui)->ip_len = m->m_len; - ((struct ip *)ui)->ip_ttl = ip_defttl; + ((struct ip *)ui)->ip_ttl = IPDEFTTL; ((struct ip *)ui)->ip_tos = iptos; - udpstat.udps_opackets++; - error = ip_output(so, m); return (error); @@ -310,74 +288,51 @@ int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *addr) { + Slirp *slirp = so->slirp; struct sockaddr_in saddr, daddr; saddr = *addr; - if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { - saddr.sin_addr.s_addr = so->so_faddr.s_addr; - if ((so->so_faddr.s_addr & htonl(0x000000ff)) == htonl(0xff)) - saddr.sin_addr.s_addr = alias_addr.s_addr; + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; + + if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { + saddr.sin_addr = slirp->vhost_addr; + } else if (addr->sin_addr.s_addr == loopback_addr.s_addr || + so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { + saddr.sin_addr = so->so_faddr; + } } daddr.sin_addr = so->so_laddr; daddr.sin_port = so->so_lport; - + return udp_output2(so, m, &saddr, &daddr, so->so_iptos); } int -udp_attach(so) - struct socket *so; +udp_attach(struct socket *so) { - struct sockaddr_in addr; - - if((so->s = socket(AF_INET,SOCK_DGRAM,0)) != -1) { - /* - * Here, we bind() the socket. Although not really needed - * (sendto() on an unbound socket will bind it), it's done - * here so that emulation of ytalk etc. don't have to do it - */ - addr.sin_family = AF_INET; - addr.sin_port = 0; - addr.sin_addr.s_addr = INADDR_ANY; - if(bind(so->s, (struct sockaddr *)&addr, sizeof(addr))<0) { - int lasterrno=errno; - closesocket(so->s); - so->s=-1; -#ifdef _WIN32 - WSASetLastError(lasterrno); -#else - errno=lasterrno; -#endif - } else { - /* success, insert in queue */ - so->so_expire = curtime + SO_EXPIRE; - insque(so,&udb); - } + if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) { + so->so_expire = curtime + SO_EXPIRE; + insque(so, &so->slirp->udb); } return(so->s); } void -udp_detach(so) - struct socket *so; +udp_detach(struct socket *so) { closesocket(so->s); - /* if (so->so_m) m_free(so->so_m); done by sofree */ - sofree(so); } -struct tos_t udptos[] = { +static const struct tos_t udptos[] = { {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */ - {517, 517, IPTOS_LOWDELAY, EMU_TALK}, /* talk */ - {518, 518, IPTOS_LOWDELAY, EMU_NTALK}, /* ntalk */ - {0, 7648, IPTOS_LOWDELAY, EMU_CUSEEME}, /* Cu-Seeme */ {0, 0, 0, 0} }; -u_int8_t -udp_tos(so) - struct socket *so; +static uint8_t +udp_tos(struct socket *so) { int i = 0; @@ -393,284 +348,47 @@ udp_tos(so) return 0; } -#ifdef EMULATE_TALK -#include "talkd.h" -#endif - -/* - * Here, talk/ytalk/ntalk requests must be emulated - */ -void -udp_emu(so, m) - struct socket *so; - struct mbuf *m; -{ - struct sockaddr_in addr; - int addrlen = sizeof(addr); -#ifdef EMULATE_TALK - CTL_MSG_OLD *omsg; - CTL_MSG *nmsg; - char buff[sizeof(CTL_MSG)]; - u_char type; - -struct talk_request { - struct talk_request *next; - struct socket *udp_so; - struct socket *tcp_so; -} *req; - - static struct talk_request *req_tbl = 0; - -#endif - -struct cu_header { - uint16_t d_family; // destination family - uint16_t d_port; // destination port - uint32_t d_addr; // destination address - uint16_t s_family; // source family - uint16_t s_port; // source port - uint32_t so_addr; // source address - uint32_t seqn; // sequence number - uint16_t message; // message - uint16_t data_type; // data type - uint16_t pkt_len; // packet length -} *cu_head; - - switch(so->so_emu) { - -#ifdef EMULATE_TALK - case EMU_TALK: - case EMU_NTALK: - /* - * Talk emulation. We always change the ctl_addr to get - * some answers from the daemon. When an ANNOUNCE comes, - * we send LEAVE_INVITE to the local daemons. Also when a - * DELETE comes, we send copies to the local daemons. - */ - if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0) - return; - -#define IS_OLD (so->so_emu == EMU_TALK) - -#define COPY_MSG(dest, src) { dest->type = src->type; \ - dest->id_num = src->id_num; \ - dest->pid = src->pid; \ - dest->addr = src->addr; \ - dest->ctl_addr = src->ctl_addr; \ - memcpy(&dest->l_name, &src->l_name, NAME_SIZE_OLD); \ - memcpy(&dest->r_name, &src->r_name, NAME_SIZE_OLD); \ - memcpy(&dest->r_tty, &src->r_tty, TTY_SIZE); } - -#define OTOSIN(ptr, field) ((struct sockaddr_in *)&ptr->field) -/* old_sockaddr to sockaddr_in */ - - - if (IS_OLD) { /* old talk */ - omsg = mtod(m, CTL_MSG_OLD*); - nmsg = (CTL_MSG *) buff; - type = omsg->type; - OTOSIN(omsg, ctl_addr)->sin_port = addr.sin_port; - OTOSIN(omsg, ctl_addr)->sin_addr = our_addr; - strncpy(omsg->l_name, getlogin(), NAME_SIZE_OLD); - } else { /* new talk */ - omsg = (CTL_MSG_OLD *) buff; - nmsg = mtod(m, CTL_MSG *); - type = nmsg->type; - OTOSIN(nmsg, ctl_addr)->sin_port = addr.sin_port; - OTOSIN(nmsg, ctl_addr)->sin_addr = our_addr; - strncpy(nmsg->l_name, getlogin(), NAME_SIZE_OLD); - } - - if (type == LOOK_UP) - return; /* for LOOK_UP this is enough */ - - if (IS_OLD) { /* make a copy of the message */ - COPY_MSG(nmsg, omsg); - nmsg->vers = 1; - nmsg->answer = 0; - } else - COPY_MSG(omsg, nmsg); - - /* - * If if is an ANNOUNCE message, we go through the - * request table to see if a tcp port has already - * been redirected for this socket. If not, we solisten() - * a new socket and add this entry to the table. - * The port number of the tcp socket and our IP - * are put to the addr field of the message structures. - * Then a LEAVE_INVITE is sent to both local daemon - * ports, 517 and 518. This is why we have two copies - * of the message, one in old talk and one in new talk - * format. - */ - - if (type == ANNOUNCE) { - int s; - u_short temp_port; - - for(req = req_tbl; req; req = req->next) - if (so == req->udp_so) - break; /* found it */ - - if (!req) { /* no entry for so, create new */ - req = (struct talk_request *) - malloc(sizeof(struct talk_request)); - req->udp_so = so; - req->tcp_so = solisten(0, - OTOSIN(omsg, addr)->sin_addr.s_addr, - OTOSIN(omsg, addr)->sin_port, - SS_FACCEPTONCE); - req->next = req_tbl; - req_tbl = req; - } - - /* replace port number in addr field */ - addrlen = sizeof(addr); - getsockname(req->tcp_so->s, - (struct sockaddr *) &addr, - &addrlen); - OTOSIN(omsg, addr)->sin_port = addr.sin_port; - OTOSIN(omsg, addr)->sin_addr = our_addr; - OTOSIN(nmsg, addr)->sin_port = addr.sin_port; - OTOSIN(nmsg, addr)->sin_addr = our_addr; - - /* send LEAVE_INVITEs */ - temp_port = OTOSIN(omsg, ctl_addr)->sin_port; - OTOSIN(omsg, ctl_addr)->sin_port = 0; - OTOSIN(nmsg, ctl_addr)->sin_port = 0; - omsg->type = nmsg->type = LEAVE_INVITE; - - s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - addr.sin_addr = our_addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(517); - sendto(s, (char *)omsg, sizeof(*omsg), 0, - (struct sockaddr *)&addr, sizeof(addr)); - addr.sin_port = htons(518); - sendto(s, (char *)nmsg, sizeof(*nmsg), 0, - (struct sockaddr *) &addr, sizeof(addr)); - closesocket(s) ; - - omsg->type = nmsg->type = ANNOUNCE; - OTOSIN(omsg, ctl_addr)->sin_port = temp_port; - OTOSIN(nmsg, ctl_addr)->sin_port = temp_port; - } - - /* - * If it is a DELETE message, we send a copy to the - * local daemons. Then we delete the entry corresponding - * to our socket from the request table. - */ - - if (type == DELETE) { - struct talk_request *temp_req, *req_next; - int s; - u_short temp_port; - - temp_port = OTOSIN(omsg, ctl_addr)->sin_port; - OTOSIN(omsg, ctl_addr)->sin_port = 0; - OTOSIN(nmsg, ctl_addr)->sin_port = 0; - - s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - addr.sin_addr = our_addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(517); - sendto(s, (char *)omsg, sizeof(*omsg), 0, - (struct sockaddr *)&addr, sizeof(addr)); - addr.sin_port = htons(518); - sendto(s, (char *)nmsg, sizeof(*nmsg), 0, - (struct sockaddr *)&addr, sizeof(addr)); - closesocket(s); - - OTOSIN(omsg, ctl_addr)->sin_port = temp_port; - OTOSIN(nmsg, ctl_addr)->sin_port = temp_port; - - /* delete table entry */ - if (so == req_tbl->udp_so) { - temp_req = req_tbl; - req_tbl = req_tbl->next; - free(temp_req); - } else { - temp_req = req_tbl; - for(req = req_tbl->next; req; req = req_next) { - req_next = req->next; - if (so == req->udp_so) { - temp_req->next = req_next; - free(req); - break; - } else { - temp_req = req; - } - } - } - } - - return; -#endif - - case EMU_CUSEEME: - - /* - * Cu-SeeMe emulation. - * Hopefully the packet is more that 16 bytes long. We don't - * do any other tests, just replace the address and port - * fields. - */ - if (m->m_len >= sizeof (*cu_head)) { - if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0) - return; - cu_head = mtod(m, struct cu_header *); - cu_head->s_port = addr.sin_port; - cu_head->so_addr = our_addr.s_addr; - } - - return; - } -} - struct socket * -udp_listen(port, laddr, lport, flags) - u_int port; - u_int32_t laddr; - u_int lport; - int flags; +udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, + u_int lport, int flags) { struct sockaddr_in addr; struct socket *so; - int addrlen = sizeof(struct sockaddr_in), opt = 1; + socklen_t addrlen = sizeof(struct sockaddr_in), opt = 1; - if ((so = socreate()) == NULL) { - free(so); - return NULL; + so = socreate(slirp); + if (!so) { + return NULL; } - so->s = socket(AF_INET,SOCK_DGRAM,0); + so->s = qemu_socket(AF_INET,SOCK_DGRAM,0); so->so_expire = curtime + SO_EXPIRE; - insque(so,&udb); + insque(so, &slirp->udb); addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = port; + addr.sin_addr.s_addr = haddr; + addr.sin_port = hport; if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) { udp_detach(so); return NULL; } - setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)); -/* setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); */ + qemu_setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)); getsockname(so->s,(struct sockaddr *)&addr,&addrlen); so->so_fport = addr.sin_port; - if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) - so->so_faddr = alias_addr; - else + if (addr.sin_addr.s_addr == 0 || + addr.sin_addr.s_addr == loopback_addr.s_addr) { + so->so_faddr = slirp->vhost_addr; + } else { 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; + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_ISFCONNECTED | flags; return so; }