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