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