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