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