1 /* 2 * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California. 3 * 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. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * from: @(#)udp_usrreq.c 7.20 (Berkeley) 4/20/91 34 * $Id: udp_usrreq.c,v 1.3 1993/05/22 11:42:52 cgd Exp $ 35 */ 36 37 #include "param.h" 38 #include "malloc.h" 39 #include "mbuf.h" 40 #include "protosw.h" 41 #include "socket.h" 42 #include "socketvar.h" 43 #include "stat.h" 44 45 #include "../net/if.h" 46 #include "../net/route.h" 47 48 #include "in.h" 49 #include "in_systm.h" 50 #include "ip.h" 51 #include "in_pcb.h" 52 #include "ip_var.h" 53 #include "ip_icmp.h" 54 #include "udp.h" 55 #include "udp_var.h" 56 57 struct inpcb *udp_last_inpcb = &udb; 58 59 /* 60 * UDP protocol implementation. 61 * Per RFC 768, August, 1980. 62 */ 63 udp_init() 64 { 65 66 udb.inp_next = udb.inp_prev = &udb; 67 } 68 69 #ifndef COMPAT_42 70 int udpcksum = 1; 71 #else 72 int udpcksum = 0; /* XXX */ 73 #endif 74 int udp_ttl = UDP_TTL; 75 76 struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; 77 78 udp_input(m, iphlen) 79 register struct mbuf *m; 80 int iphlen; 81 { 82 register struct ip *ip; 83 register struct udphdr *uh; 84 register struct inpcb *inp; 85 struct mbuf *opts = 0; 86 int len; 87 struct ip save_ip; 88 89 udpstat.udps_ipackets++; 90 91 /* 92 * Strip IP options, if any; should skip this, 93 * make available to user, and use on returned packets, 94 * but we don't yet have a way to check the checksum 95 * with options still present. 96 */ 97 if (iphlen > sizeof (struct ip)) { 98 ip_stripoptions(m, (struct mbuf *)0); 99 iphlen = sizeof(struct ip); 100 } 101 102 /* 103 * Get IP and UDP header together in first mbuf. 104 */ 105 ip = mtod(m, struct ip *); 106 if (m->m_len < iphlen + sizeof(struct udphdr)) { 107 if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { 108 udpstat.udps_hdrops++; 109 return; 110 } 111 ip = mtod(m, struct ip *); 112 } 113 uh = (struct udphdr *)((caddr_t)ip + iphlen); 114 115 /* 116 * Make mbuf data length reflect UDP length. 117 * If not enough data to reflect UDP length, drop. 118 */ 119 len = ntohs((u_short)uh->uh_ulen); 120 if (ip->ip_len != len) { 121 if (len > ip->ip_len) { 122 udpstat.udps_badlen++; 123 goto bad; 124 } 125 m_adj(m, len - ip->ip_len); 126 /* ip->ip_len = len; */ 127 } 128 /* 129 * Save a copy of the IP header in case we want restore it 130 * for sending an ICMP error message in response. 131 */ 132 save_ip = *ip; 133 134 /* 135 * Checksum extended UDP header and data. 136 */ 137 if (udpcksum && uh->uh_sum) { 138 ((struct ipovly *)ip)->ih_next = 0; 139 ((struct ipovly *)ip)->ih_prev = 0; 140 ((struct ipovly *)ip)->ih_x1 = 0; 141 ((struct ipovly *)ip)->ih_len = uh->uh_ulen; 142 if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) { 143 udpstat.udps_badsum++; 144 m_freem(m); 145 return; 146 } 147 } 148 149 /* 150 * Locate pcb for datagram. 151 */ 152 inp = udp_last_inpcb; 153 if (inp->inp_lport != uh->uh_dport || 154 inp->inp_fport != uh->uh_sport || 155 inp->inp_faddr.s_addr != ip->ip_src.s_addr || 156 inp->inp_laddr.s_addr != ip->ip_dst.s_addr) { 157 inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport, 158 ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD); 159 if (inp) 160 udp_last_inpcb = inp; 161 udpstat.udpps_pcbcachemiss++; 162 } 163 if (inp == 0) { 164 /* don't send ICMP response for broadcast packet */ 165 udpstat.udps_noport++; 166 if (m->m_flags & M_BCAST) { 167 udpstat.udps_noportbcast++; 168 goto bad; 169 } 170 *ip = save_ip; 171 ip->ip_len += iphlen; 172 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 173 return; 174 } 175 176 /* 177 * Construct sockaddr format source address. 178 * Stuff source address and datagram in user buffer. 179 */ 180 udp_in.sin_port = uh->uh_sport; 181 udp_in.sin_addr = ip->ip_src; 182 if (inp->inp_flags & INP_CONTROLOPTS) { 183 struct mbuf **mp = &opts; 184 struct mbuf *udp_saveopt(); 185 186 if (inp->inp_flags & INP_RECVDSTADDR) { 187 *mp = udp_saveopt((caddr_t) &ip->ip_dst, 188 sizeof(struct in_addr), IP_RECVDSTADDR); 189 if (*mp) 190 mp = &(*mp)->m_next; 191 } 192 #ifdef notyet 193 /* options were tossed above */ 194 if (inp->inp_flags & INP_RECVOPTS) { 195 *mp = udp_saveopt((caddr_t) opts_deleted_above, 196 sizeof(struct in_addr), IP_RECVOPTS); 197 if (*mp) 198 mp = &(*mp)->m_next; 199 } 200 /* ip_srcroute doesn't do what we want here, need to fix */ 201 if (inp->inp_flags & INP_RECVRETOPTS) { 202 *mp = udp_saveopt((caddr_t) ip_srcroute(), 203 sizeof(struct in_addr), IP_RECVRETOPTS); 204 if (*mp) 205 mp = &(*mp)->m_next; 206 } 207 #endif 208 } 209 iphlen += sizeof(struct udphdr); 210 m->m_len -= iphlen; 211 m->m_pkthdr.len -= iphlen; 212 m->m_data += iphlen; 213 if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 214 m, opts) == 0) { 215 udpstat.udps_fullsock++; 216 goto bad; 217 } 218 sorwakeup(inp->inp_socket); 219 return; 220 bad: 221 m_freem(m); 222 if (opts) 223 m_freem(opts); 224 } 225 226 /* 227 * Create a "control" mbuf containing the specified data 228 * with the specified type for presentation with a datagram. 229 */ 230 struct mbuf * 231 udp_saveopt(p, size, type) 232 caddr_t p; 233 register int size; 234 int type; 235 { 236 register struct cmsghdr *cp; 237 struct mbuf *m; 238 239 if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) 240 return ((struct mbuf *) NULL); 241 cp = (struct cmsghdr *) mtod(m, struct cmsghdr *); 242 bcopy(p, (caddr_t)(cp + 1), size); 243 size += sizeof(*cp); 244 m->m_len = size; 245 cp->cmsg_len = size; 246 cp->cmsg_level = IPPROTO_IP; 247 cp->cmsg_type = type; 248 return (m); 249 } 250 251 /* 252 * Notify a udp user of an asynchronous error; 253 * just wake up so that he can collect error status. 254 */ 255 udp_notify(inp, errno) 256 register struct inpcb *inp; 257 { 258 259 inp->inp_socket->so_error = errno; 260 sorwakeup(inp->inp_socket); 261 sowwakeup(inp->inp_socket); 262 } 263 264 udp_ctlinput(cmd, sa, ip) 265 int cmd; 266 struct sockaddr *sa; 267 register struct ip *ip; 268 { 269 register struct udphdr *uh; 270 extern struct in_addr zeroin_addr; 271 extern u_char inetctlerrmap[]; 272 273 if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0) 274 return; 275 if (ip) { 276 uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 277 in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, 278 cmd, udp_notify); 279 } else 280 in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); 281 } 282 283 udp_output(inp, m, addr, control) 284 register struct inpcb *inp; 285 register struct mbuf *m; 286 struct mbuf *addr, *control; 287 { 288 register struct udpiphdr *ui; 289 register int len = m->m_pkthdr.len; 290 struct in_addr laddr; 291 int s, error = 0; 292 293 if (control) 294 m_freem(control); /* XXX */ 295 296 if (addr) { 297 laddr = inp->inp_laddr; 298 if (inp->inp_faddr.s_addr != INADDR_ANY) { 299 error = EISCONN; 300 goto release; 301 } 302 /* 303 * Must block input while temporarily connected. 304 */ 305 s = splnet(); 306 error = in_pcbconnect(inp, addr); 307 if (error) { 308 splx(s); 309 goto release; 310 } 311 } else { 312 if (inp->inp_faddr.s_addr == INADDR_ANY) { 313 error = ENOTCONN; 314 goto release; 315 } 316 } 317 /* 318 * Calculate data length and get a mbuf 319 * for UDP and IP headers. 320 */ 321 M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT); 322 323 /* 324 * Fill in mbuf with extended UDP header 325 * and addresses and length put into network format. 326 */ 327 ui = mtod(m, struct udpiphdr *); 328 ui->ui_next = ui->ui_prev = 0; 329 ui->ui_x1 = 0; 330 ui->ui_pr = IPPROTO_UDP; 331 ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 332 ui->ui_src = inp->inp_laddr; 333 ui->ui_dst = inp->inp_faddr; 334 ui->ui_sport = inp->inp_lport; 335 ui->ui_dport = inp->inp_fport; 336 ui->ui_ulen = ui->ui_len; 337 338 /* 339 * Stuff checksum and output datagram. 340 */ 341 ui->ui_sum = 0; 342 if (udpcksum) { 343 if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 344 ui->ui_sum = 0xffff; 345 } 346 ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 347 ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */ 348 ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ 349 udpstat.udps_opackets++; 350 error = ip_output(m, inp->inp_options, &inp->inp_route, 351 inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)); 352 353 if (addr) { 354 in_pcbdisconnect(inp); 355 inp->inp_laddr = laddr; 356 splx(s); 357 } 358 return (error); 359 360 release: 361 m_freem(m); 362 return (error); 363 } 364 365 u_long udp_sendspace = 9216; /* really max datagram size */ 366 u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); 367 /* 40 1K datagrams */ 368 369 /*ARGSUSED*/ 370 udp_usrreq(so, req, m, addr, control) 371 struct socket *so; 372 int req; 373 struct mbuf *m, *addr, *control; 374 { 375 struct inpcb *inp = sotoinpcb(so); 376 int error = 0; 377 int s; 378 379 if (req == PRU_CONTROL) 380 return (in_control(so, (int)m, (caddr_t)addr, 381 (struct ifnet *)control)); 382 if (inp == NULL && req != PRU_ATTACH) { 383 error = EINVAL; 384 goto release; 385 } 386 /* 387 * Note: need to block udp_input while changing 388 * the udp pcb queue and/or pcb addresses. 389 */ 390 switch (req) { 391 392 case PRU_ATTACH: 393 if (inp != NULL) { 394 error = EINVAL; 395 break; 396 } 397 s = splnet(); 398 error = in_pcballoc(so, &udb); 399 splx(s); 400 if (error) 401 break; 402 error = soreserve(so, udp_sendspace, udp_recvspace); 403 if (error) 404 break; 405 ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 406 break; 407 408 case PRU_DETACH: 409 udp_detach(inp); 410 break; 411 412 case PRU_BIND: 413 s = splnet(); 414 error = in_pcbbind(inp, addr); 415 splx(s); 416 break; 417 418 case PRU_LISTEN: 419 error = EOPNOTSUPP; 420 break; 421 422 case PRU_CONNECT: 423 if (inp->inp_faddr.s_addr != INADDR_ANY) { 424 error = EISCONN; 425 break; 426 } 427 s = splnet(); 428 error = in_pcbconnect(inp, addr); 429 splx(s); 430 if (error == 0) 431 soisconnected(so); 432 break; 433 434 case PRU_CONNECT2: 435 error = EOPNOTSUPP; 436 break; 437 438 case PRU_ACCEPT: 439 error = EOPNOTSUPP; 440 break; 441 442 case PRU_DISCONNECT: 443 if (inp->inp_faddr.s_addr == INADDR_ANY) { 444 error = ENOTCONN; 445 break; 446 } 447 s = splnet(); 448 in_pcbdisconnect(inp); 449 inp->inp_laddr.s_addr = INADDR_ANY; 450 splx(s); 451 so->so_state &= ~SS_ISCONNECTED; /* XXX */ 452 break; 453 454 case PRU_SHUTDOWN: 455 socantsendmore(so); 456 break; 457 458 case PRU_SEND: 459 return (udp_output(inp, m, addr, control)); 460 461 case PRU_ABORT: 462 soisdisconnected(so); 463 udp_detach(inp); 464 break; 465 466 case PRU_SOCKADDR: 467 in_setsockaddr(inp, addr); 468 break; 469 470 case PRU_PEERADDR: 471 in_setpeeraddr(inp, addr); 472 break; 473 474 case PRU_SENSE: 475 /* 476 * stat: don't bother with a blocksize. 477 */ 478 return (0); 479 480 case PRU_SENDOOB: 481 case PRU_FASTTIMO: 482 case PRU_SLOWTIMO: 483 case PRU_PROTORCV: 484 case PRU_PROTOSEND: 485 error = EOPNOTSUPP; 486 break; 487 488 case PRU_RCVD: 489 case PRU_RCVOOB: 490 return (EOPNOTSUPP); /* do not free mbuf's */ 491 492 default: 493 panic("udp_usrreq"); 494 } 495 496 release: 497 if (control) { 498 printf("udp control data unexpectedly retained\n"); 499 m_freem(control); 500 } 501 if (m) 502 m_freem(m); 503 return (error); 504 } 505 506 udp_detach(inp) 507 struct inpcb *inp; 508 { 509 int s = splnet(); 510 511 if (inp == udp_last_inpcb) 512 udp_last_inpcb = &udb; 513 in_pcbdetach(inp); 514 splx(s); 515 } 516