1 /* 2 * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 * 17 * @(#)udp_usrreq.c 7.12 (Berkeley) 06/27/89 18 */ 19 20 #include "param.h" 21 #include "user.h" 22 #include "malloc.h" 23 #include "mbuf.h" 24 #include "protosw.h" 25 #include "socket.h" 26 #include "socketvar.h" 27 #include "errno.h" 28 #include "stat.h" 29 30 #include "../net/if.h" 31 #include "../net/route.h" 32 33 #include "in.h" 34 #include "in_pcb.h" 35 #include "in_systm.h" 36 #include "ip.h" 37 #include "ip_var.h" 38 #include "ip_icmp.h" 39 #include "udp.h" 40 #include "udp_var.h" 41 42 /* 43 * UDP protocol implementation. 44 * Per RFC 768, August, 1980. 45 */ 46 udp_init() 47 { 48 49 udb.inp_next = udb.inp_prev = &udb; 50 } 51 52 #ifndef COMPAT_42 53 int udpcksum = 1; 54 #else 55 int udpcksum = 0; /* XXX */ 56 #endif 57 int udp_ttl = UDP_TTL; 58 59 struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; 60 61 udp_input(m, iphlen) 62 register struct mbuf *m; 63 int iphlen; 64 { 65 register struct udpiphdr *ui; 66 register struct inpcb *inp; 67 int len; 68 struct ip ip; 69 70 /* 71 * Get IP and UDP header together in first mbuf. 72 * Note: IP leaves IP header in first mbuf. 73 */ 74 ui = mtod(m, struct udpiphdr *); 75 if (iphlen > sizeof (struct ip)) 76 ip_stripoptions(m, (struct mbuf *)0); 77 if (m->m_len < sizeof (struct udpiphdr)) { 78 if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 79 udpstat.udps_hdrops++; 80 return; 81 } 82 ui = mtod(m, struct udpiphdr *); 83 } 84 85 /* 86 * Make mbuf data length reflect UDP length. 87 * If not enough data to reflect UDP length, drop. 88 */ 89 len = ntohs((u_short)ui->ui_ulen); 90 if (((struct ip *)ui)->ip_len != len) { 91 if (len > ((struct ip *)ui)->ip_len) { 92 udpstat.udps_badlen++; 93 goto bad; 94 } 95 m_adj(m, len - ((struct ip *)ui)->ip_len); 96 /* ((struct ip *)ui)->ip_len = len; */ 97 } 98 /* 99 * Save a copy of the IP header in case we want restore it for ICMP. 100 */ 101 ip = *(struct ip *)ui; 102 103 /* 104 * Checksum extended UDP header and data. 105 */ 106 if (udpcksum && ui->ui_sum) { 107 ui->ui_next = ui->ui_prev = 0; 108 ui->ui_x1 = 0; 109 ui->ui_len = ui->ui_ulen; 110 if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 111 udpstat.udps_badsum++; 112 m_freem(m); 113 return; 114 } 115 } 116 117 /* 118 * Locate pcb for datagram. 119 */ 120 inp = in_pcblookup(&udb, 121 ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 122 INPLOOKUP_WILDCARD); 123 if (inp == 0) { 124 /* don't send ICMP response for broadcast packet */ 125 if (m->m_flags & M_BCAST) 126 goto bad; 127 *(struct ip *)ui = ip; 128 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 129 return; 130 } 131 132 /* 133 * Construct sockaddr format source address. 134 * Stuff source address and datagram in user buffer. 135 */ 136 udp_in.sin_port = ui->ui_sport; 137 udp_in.sin_addr = ui->ui_src; 138 m->m_len -= sizeof (struct udpiphdr); 139 m->m_pkthdr.len -= sizeof (struct udpiphdr); 140 m->m_data += sizeof (struct udpiphdr); 141 if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 142 m, (struct mbuf *)0) == 0) 143 goto bad; 144 sorwakeup(inp->inp_socket); 145 return; 146 bad: 147 m_freem(m); 148 } 149 150 /* 151 * Notify a udp user of an asynchronous error; 152 * just wake up so that he can collect error status. 153 */ 154 udp_notify(inp) 155 register struct inpcb *inp; 156 { 157 158 sorwakeup(inp->inp_socket); 159 sowwakeup(inp->inp_socket); 160 } 161 162 udp_ctlinput(cmd, sa) 163 int cmd; 164 struct sockaddr *sa; 165 { 166 extern u_char inetctlerrmap[]; 167 struct sockaddr_in *sin; 168 int in_rtchange(); 169 170 if ((unsigned)cmd > PRC_NCMDS) 171 return; 172 if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) 173 return; 174 sin = (struct sockaddr_in *)sa; 175 if (sin->sin_addr.s_addr == INADDR_ANY) 176 return; 177 178 switch (cmd) { 179 180 case PRC_QUENCH: 181 break; 182 183 case PRC_ROUTEDEAD: 184 case PRC_REDIRECT_NET: 185 case PRC_REDIRECT_HOST: 186 case PRC_REDIRECT_TOSNET: 187 case PRC_REDIRECT_TOSHOST: 188 in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange); 189 break; 190 191 default: 192 if (inetctlerrmap[cmd] == 0) 193 return; /* XXX */ 194 in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd], 195 udp_notify); 196 } 197 } 198 199 udp_output(inp, m) 200 register struct inpcb *inp; 201 register struct mbuf *m; 202 { 203 register struct udpiphdr *ui; 204 register int len = m->m_pkthdr.len; 205 206 /* 207 * Calculate data length and get a mbuf 208 * for UDP and IP headers. 209 */ 210 M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT); 211 212 /* 213 * Fill in mbuf with extended UDP header 214 * and addresses and length put into network format. 215 */ 216 ui = mtod(m, struct udpiphdr *); 217 ui->ui_next = ui->ui_prev = 0; 218 ui->ui_x1 = 0; 219 ui->ui_pr = IPPROTO_UDP; 220 ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 221 ui->ui_src = inp->inp_laddr; 222 ui->ui_dst = inp->inp_faddr; 223 ui->ui_sport = inp->inp_lport; 224 ui->ui_dport = inp->inp_fport; 225 ui->ui_ulen = ui->ui_len; 226 227 /* 228 * Stuff checksum and output datagram. 229 */ 230 ui->ui_sum = 0; 231 if (udpcksum) { 232 if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 233 ui->ui_sum = 0xffff; 234 } 235 ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 236 ((struct ip *)ui)->ip_ttl = udp_ttl; 237 return (ip_output(m, inp->inp_options, &inp->inp_route, 238 inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); 239 } 240 241 u_long udp_sendspace = 9216; /* really max datagram size */ 242 u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in)); 243 /* 40 1K datagrams */ 244 245 /*ARGSUSED*/ 246 udp_usrreq(so, req, m, nam, rights, control) 247 struct socket *so; 248 int req; 249 struct mbuf *m, *nam, *rights, *control; 250 { 251 struct inpcb *inp = sotoinpcb(so); 252 int error = 0; 253 254 if (req == PRU_CONTROL) 255 return (in_control(so, (int)m, (caddr_t)nam, 256 (struct ifnet *)rights)); 257 if (rights && rights->m_len) { 258 error = EINVAL; 259 goto release; 260 } 261 if (inp == NULL && req != PRU_ATTACH) { 262 error = EINVAL; 263 goto release; 264 } 265 switch (req) { 266 267 case PRU_ATTACH: 268 if (inp != NULL) { 269 error = EINVAL; 270 break; 271 } 272 error = in_pcballoc(so, &udb); 273 if (error) 274 break; 275 error = soreserve(so, udp_sendspace, udp_recvspace); 276 if (error) 277 break; 278 break; 279 280 case PRU_DETACH: 281 in_pcbdetach(inp); 282 break; 283 284 case PRU_BIND: 285 error = in_pcbbind(inp, nam); 286 break; 287 288 case PRU_LISTEN: 289 error = EOPNOTSUPP; 290 break; 291 292 case PRU_CONNECT: 293 if (inp->inp_faddr.s_addr != INADDR_ANY) { 294 error = EISCONN; 295 break; 296 } 297 error = in_pcbconnect(inp, nam); 298 if (error == 0) 299 soisconnected(so); 300 break; 301 302 case PRU_CONNECT2: 303 error = EOPNOTSUPP; 304 break; 305 306 case PRU_ACCEPT: 307 error = EOPNOTSUPP; 308 break; 309 310 case PRU_DISCONNECT: 311 if (inp->inp_faddr.s_addr == INADDR_ANY) { 312 error = ENOTCONN; 313 break; 314 } 315 in_pcbdisconnect(inp); 316 so->so_state &= ~SS_ISCONNECTED; /* XXX */ 317 break; 318 319 case PRU_SHUTDOWN: 320 socantsendmore(so); 321 break; 322 323 case PRU_SEND: { 324 struct in_addr laddr; 325 int s; 326 327 if (nam) { 328 laddr = inp->inp_laddr; 329 if (inp->inp_faddr.s_addr != INADDR_ANY) { 330 error = EISCONN; 331 break; 332 } 333 /* 334 * Must block input while temporarily connected. 335 */ 336 s = splnet(); 337 error = in_pcbconnect(inp, nam); 338 if (error) { 339 splx(s); 340 break; 341 } 342 } else { 343 if (inp->inp_faddr.s_addr == INADDR_ANY) { 344 error = ENOTCONN; 345 break; 346 } 347 } 348 error = udp_output(inp, m); 349 m = NULL; 350 if (nam) { 351 in_pcbdisconnect(inp); 352 inp->inp_laddr = laddr; 353 splx(s); 354 } 355 } 356 break; 357 358 case PRU_ABORT: 359 soisdisconnected(so); 360 in_pcbdetach(inp); 361 break; 362 363 case PRU_SOCKADDR: 364 in_setsockaddr(inp, nam); 365 break; 366 367 case PRU_PEERADDR: 368 in_setpeeraddr(inp, nam); 369 break; 370 371 case PRU_SENSE: 372 /* 373 * stat: don't bother with a blocksize. 374 */ 375 return (0); 376 377 case PRU_SENDOOB: 378 case PRU_FASTTIMO: 379 case PRU_SLOWTIMO: 380 case PRU_PROTORCV: 381 case PRU_PROTOSEND: 382 error = EOPNOTSUPP; 383 break; 384 385 case PRU_RCVD: 386 case PRU_RCVOOB: 387 return (EOPNOTSUPP); /* do not free mbuf's */ 388 389 default: 390 panic("udp_usrreq"); 391 } 392 release: 393 if (m != NULL) 394 m_freem(m); 395 return (error); 396 } 397