1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /*
3  * Copyright (c) 1982, 1986, 1988, 1990, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *	@(#)udp_usrreq.c	8.4 (Berkeley) 1/21/94
31  * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
32  */
33 
34 /*
35  * Changes and additions relating to SLiRP
36  * Copyright (c) 1995 Danny Gasparovski.
37  *
38  * Please read the file COPYRIGHT for the
39  * terms and conditions of the copyright.
40  */
41 
42 #include "slirp.h"
43 #include "ip_icmp.h"
44 
45 static uint8_t udp_tos(struct socket *so);
46 
udp_init(Slirp * slirp)47 void udp_init(Slirp *slirp)
48 {
49     slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb;
50     slirp->udp_last_so = &slirp->udb;
51 }
52 
udp_cleanup(Slirp * slirp)53 void udp_cleanup(Slirp *slirp)
54 {
55     while (slirp->udb.so_next != &slirp->udb) {
56         udp_detach(slirp->udb.so_next);
57     }
58 }
59 
60 /* m->m_data  points at ip packet header
61  * m->m_len   length ip packet
62  * ip->ip_len length data (IPDU)
63  */
udp_input(register struct mbuf * m,int iphlen)64 void udp_input(register struct mbuf *m, int iphlen)
65 {
66     Slirp *slirp = m->slirp;
67     register struct ip *ip;
68     register struct udphdr *uh;
69     int len;
70     struct ip save_ip;
71     struct socket *so;
72     struct sockaddr_storage lhost;
73     struct sockaddr_in *lhost4;
74 
75     DEBUG_CALL("udp_input");
76     DEBUG_ARG("m = %p", m);
77     DEBUG_ARG("iphlen = %d", iphlen);
78 
79     /*
80      * Strip IP options, if any; should skip this,
81      * make available to user, and use on returned packets,
82      * but we don't yet have a way to check the checksum
83      * with options still present.
84      */
85     if (iphlen > sizeof(struct ip)) {
86         ip_stripoptions(m, (struct mbuf *)0);
87         iphlen = sizeof(struct ip);
88     }
89 
90     /*
91      * Get IP and UDP header together in first mbuf.
92      */
93     ip = mtod(m, struct ip *);
94     uh = (struct udphdr *)((char *)ip + iphlen);
95 
96     /*
97      * Make mbuf data length reflect UDP length.
98      * If not enough data to reflect UDP length, drop.
99      */
100     len = ntohs((uint16_t)uh->uh_ulen);
101 
102     if (ip->ip_len != len) {
103         if (len > ip->ip_len) {
104             goto bad;
105         }
106         m_adj(m, len - ip->ip_len);
107         ip->ip_len = len;
108     }
109 
110     /*
111      * Save a copy of the IP header in case we want restore it
112      * for sending an ICMP error message in response.
113      */
114     save_ip = *ip;
115     save_ip.ip_len += iphlen; /* tcp_input subtracts this */
116 
117     /*
118      * Checksum extended UDP header and data.
119      */
120     if (uh->uh_sum) {
121         memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr));
122         ((struct ipovly *)ip)->ih_x1 = 0;
123         ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
124         if (cksum(m, len + sizeof(struct ip))) {
125             goto bad;
126         }
127     }
128 
129     lhost.ss_family = AF_INET;
130     lhost4 = (struct sockaddr_in *)&lhost;
131     lhost4->sin_addr = ip->ip_src;
132     lhost4->sin_port = uh->uh_sport;
133 
134     /*
135      *  handle DHCP/BOOTP
136      */
137     if (ntohs(uh->uh_dport) == BOOTP_SERVER &&
138         (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr ||
139          ip->ip_dst.s_addr == 0xffffffff)) {
140         bootp_input(m);
141         goto bad;
142     }
143 
144     /*
145      *  handle TFTP
146      */
147     if (ntohs(uh->uh_dport) == TFTP_SERVER &&
148         ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
149         m->m_data += iphlen;
150         m->m_len -= iphlen;
151         tftp_input(&lhost, m);
152         m->m_data -= iphlen;
153         m->m_len += iphlen;
154         goto bad;
155     }
156 
157     if (slirp->restricted) {
158         goto bad;
159     }
160 
161     /*
162      * Locate pcb for datagram.
163      */
164     so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL);
165 
166     if (so == NULL) {
167         /*
168          * If there's no socket for this packet,
169          * create one
170          */
171         so = socreate(slirp);
172         if (udp_attach(so, AF_INET) == -1) {
173             DEBUG_MISC(" udp_attach errno = %d-%s", errno, strerror(errno));
174             sofree(so);
175             goto bad;
176         }
177 
178         /*
179          * Setup fields
180          */
181         so->so_lfamily = AF_INET;
182         so->so_laddr = ip->ip_src;
183         so->so_lport = uh->uh_sport;
184 
185         if ((so->so_iptos = udp_tos(so)) == 0)
186             so->so_iptos = ip->ip_tos;
187 
188         /*
189          * XXXXX Here, check if it's in udpexec_list,
190          * and if it is, do the fork_exec() etc.
191          */
192     }
193 
194     so->so_ffamily = AF_INET;
195     so->so_faddr = ip->ip_dst; /* XXX */
196     so->so_fport = uh->uh_dport; /* XXX */
197 
198     iphlen += sizeof(struct udphdr);
199     m->m_len -= iphlen;
200     m->m_data += iphlen;
201 
202     /*
203      * Now we sendto() the packet.
204      */
205     if (sosendto(so, m) == -1) {
206         m->m_len += iphlen;
207         m->m_data -= iphlen;
208         *ip = save_ip;
209         DEBUG_MISC("udp tx errno = %d-%s", errno, strerror(errno));
210         icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
211         goto bad;
212     }
213 
214     m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
215 
216     /* restore the orig mbuf packet */
217     m->m_len += iphlen;
218     m->m_data -= iphlen;
219     *ip = save_ip;
220     so->so_m = m; /* ICMP backup */
221 
222     return;
223 bad:
224     m_free(m);
225 }
226 
udp_output(struct socket * so,struct mbuf * m,struct sockaddr_in * saddr,struct sockaddr_in * daddr,int iptos)227 int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr,
228                struct sockaddr_in *daddr, int iptos)
229 {
230     register struct udpiphdr *ui;
231     int error = 0;
232 
233     DEBUG_CALL("udp_output");
234     DEBUG_ARG("so = %p", so);
235     DEBUG_ARG("m = %p", m);
236     DEBUG_ARG("saddr = %s", inet_ntoa(saddr->sin_addr));
237     DEBUG_ARG("daddr = %s", inet_ntoa(daddr->sin_addr));
238 
239     /*
240      * Adjust for header
241      */
242     m->m_data -= sizeof(struct udpiphdr);
243     m->m_len += sizeof(struct udpiphdr);
244 
245     /*
246      * Fill in mbuf with extended UDP header
247      * and addresses and length put into network format.
248      */
249     ui = mtod(m, struct udpiphdr *);
250     memset(&ui->ui_i.ih_mbuf, 0, sizeof(struct mbuf_ptr));
251     ui->ui_x1 = 0;
252     ui->ui_pr = IPPROTO_UDP;
253     ui->ui_len = htons(m->m_len - sizeof(struct ip));
254     /* XXXXX Check for from-one-location sockets, or from-any-location sockets
255      */
256     ui->ui_src = saddr->sin_addr;
257     ui->ui_dst = daddr->sin_addr;
258     ui->ui_sport = saddr->sin_port;
259     ui->ui_dport = daddr->sin_port;
260     ui->ui_ulen = ui->ui_len;
261 
262     /*
263      * Stuff checksum and output datagram.
264      */
265     ui->ui_sum = 0;
266     if ((ui->ui_sum = cksum(m, m->m_len)) == 0)
267         ui->ui_sum = 0xffff;
268     ((struct ip *)ui)->ip_len = m->m_len;
269 
270     ((struct ip *)ui)->ip_ttl = IPDEFTTL;
271     ((struct ip *)ui)->ip_tos = iptos;
272 
273     error = ip_output(so, m);
274 
275     return (error);
276 }
277 
udp_attach(struct socket * so,unsigned short af)278 int udp_attach(struct socket *so, unsigned short af)
279 {
280     so->s = slirp_socket(af, SOCK_DGRAM, 0);
281     if (so->s != -1) {
282         so->so_expire = curtime + SO_EXPIRE;
283         insque(so, &so->slirp->udb);
284     }
285     return (so->s);
286 }
287 
udp_detach(struct socket * so)288 void udp_detach(struct socket *so)
289 {
290     so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);
291     closesocket(so->s);
292     sofree(so);
293 }
294 
295 static const struct tos_t udptos[] = { { 0, 53, IPTOS_LOWDELAY, 0 }, /* DNS */
296                                        { 0, 0, 0, 0 } };
297 
udp_tos(struct socket * so)298 static uint8_t udp_tos(struct socket *so)
299 {
300     int i = 0;
301 
302     while (udptos[i].tos) {
303         if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) ||
304             (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) {
305             if (so->slirp->enable_emu)
306                 so->so_emu = udptos[i].emu;
307             return udptos[i].tos;
308         }
309         i++;
310     }
311 
312     return 0;
313 }
314 
udp_listen(Slirp * slirp,uint32_t haddr,unsigned hport,uint32_t laddr,unsigned lport,int flags)315 struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport,
316                           uint32_t laddr, unsigned lport, int flags)
317 {
318     /* TODO: IPv6 */
319     struct sockaddr_in addr;
320     struct socket *so;
321     socklen_t addrlen = sizeof(struct sockaddr_in);
322 
323     so = socreate(slirp);
324     so->s = slirp_socket(AF_INET, SOCK_DGRAM, 0);
325     if (so->s < 0) {
326         sofree(so);
327         return NULL;
328     }
329     so->so_expire = curtime + SO_EXPIRE;
330     insque(so, &slirp->udb);
331 
332     addr.sin_family = AF_INET;
333     addr.sin_addr.s_addr = haddr;
334     addr.sin_port = hport;
335 
336     if (bind(so->s, (struct sockaddr *)&addr, addrlen) < 0) {
337         udp_detach(so);
338         return NULL;
339     }
340     slirp_socket_set_fast_reuse(so->s);
341 
342     getsockname(so->s, (struct sockaddr *)&addr, &addrlen);
343     so->fhost.sin = addr;
344     sotranslate_accept(so);
345     so->so_lfamily = AF_INET;
346     so->so_lport = lport;
347     so->so_laddr.s_addr = laddr;
348     if (flags != SS_FACCEPTONCE)
349         so->so_expire = 0;
350 
351     so->so_state &= SS_PERSISTENT_MASK;
352     so->so_state |= SS_ISFCONNECTED | flags;
353 
354     return so;
355 }
356