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.17 (Berkeley) 07/01/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 + size, 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 * block udp_input while changing udp pcb queue, 363 * addresses; should be done for individual cases, 364 * but it's not worth it. 365 */ 366 s = splnet(); 367 switch (req) { 368 369 case PRU_ATTACH: 370 if (inp != NULL) { 371 error = EINVAL; 372 break; 373 } 374 s = splnet(); 375 error = in_pcballoc(so, &udb); 376 splx(s); 377 if (error) 378 break; 379 error = soreserve(so, udp_sendspace, udp_recvspace); 380 if (error) 381 break; 382 ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl; 383 break; 384 385 case PRU_DETACH: 386 in_pcbdetach(inp); 387 break; 388 389 case PRU_BIND: 390 error = in_pcbbind(inp, addr); 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 error = in_pcbconnect(inp, addr); 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 in_pcbdisconnect(inp); 421 inp->inp_laddr.s_addr = INADDR_ANY; 422 so->so_state &= ~SS_ISCONNECTED; /* XXX */ 423 break; 424 425 case PRU_SHUTDOWN: 426 socantsendmore(so); 427 break; 428 429 case PRU_SEND: 430 splx(s); 431 return (udp_output(inp, m, addr, control)); 432 433 case PRU_ABORT: 434 soisdisconnected(so); 435 in_pcbdetach(inp); 436 break; 437 438 case PRU_SOCKADDR: 439 in_setsockaddr(inp, addr); 440 break; 441 442 case PRU_PEERADDR: 443 in_setpeeraddr(inp, addr); 444 break; 445 446 case PRU_SENSE: 447 /* 448 * stat: don't bother with a blocksize. 449 */ 450 return (0); 451 452 case PRU_SENDOOB: 453 case PRU_FASTTIMO: 454 case PRU_SLOWTIMO: 455 case PRU_PROTORCV: 456 case PRU_PROTOSEND: 457 error = EOPNOTSUPP; 458 break; 459 460 case PRU_RCVD: 461 case PRU_RCVOOB: 462 return (EOPNOTSUPP); /* do not free mbuf's */ 463 464 default: 465 panic("udp_usrreq"); 466 } 467 splx(s); 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