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.11 (Berkeley) 05/05/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 = 2048; /* really max datagram size */ 242 u_long udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */ 243 244 /*ARGSUSED*/ 245 udp_usrreq(so, req, m, nam, rights, control) 246 struct socket *so; 247 int req; 248 struct mbuf *m, *nam, *rights, *control; 249 { 250 struct inpcb *inp = sotoinpcb(so); 251 int error = 0; 252 253 if (req == PRU_CONTROL) 254 return (in_control(so, (int)m, (caddr_t)nam, 255 (struct ifnet *)rights)); 256 if (rights && rights->m_len) { 257 error = EINVAL; 258 goto release; 259 } 260 if (inp == NULL && req != PRU_ATTACH) { 261 error = EINVAL; 262 goto release; 263 } 264 switch (req) { 265 266 case PRU_ATTACH: 267 if (inp != NULL) { 268 error = EINVAL; 269 break; 270 } 271 error = in_pcballoc(so, &udb); 272 if (error) 273 break; 274 error = soreserve(so, udp_sendspace, udp_recvspace); 275 if (error) 276 break; 277 break; 278 279 case PRU_DETACH: 280 in_pcbdetach(inp); 281 break; 282 283 case PRU_BIND: 284 error = in_pcbbind(inp, nam); 285 break; 286 287 case PRU_LISTEN: 288 error = EOPNOTSUPP; 289 break; 290 291 case PRU_CONNECT: 292 if (inp->inp_faddr.s_addr != INADDR_ANY) { 293 error = EISCONN; 294 break; 295 } 296 error = in_pcbconnect(inp, nam); 297 if (error == 0) 298 soisconnected(so); 299 break; 300 301 case PRU_CONNECT2: 302 error = EOPNOTSUPP; 303 break; 304 305 case PRU_ACCEPT: 306 error = EOPNOTSUPP; 307 break; 308 309 case PRU_DISCONNECT: 310 if (inp->inp_faddr.s_addr == INADDR_ANY) { 311 error = ENOTCONN; 312 break; 313 } 314 in_pcbdisconnect(inp); 315 so->so_state &= ~SS_ISCONNECTED; /* XXX */ 316 break; 317 318 case PRU_SHUTDOWN: 319 socantsendmore(so); 320 break; 321 322 case PRU_SEND: { 323 struct in_addr laddr; 324 int s; 325 326 if (nam) { 327 laddr = inp->inp_laddr; 328 if (inp->inp_faddr.s_addr != INADDR_ANY) { 329 error = EISCONN; 330 break; 331 } 332 /* 333 * Must block input while temporarily connected. 334 */ 335 s = splnet(); 336 error = in_pcbconnect(inp, nam); 337 if (error) { 338 splx(s); 339 break; 340 } 341 } else { 342 if (inp->inp_faddr.s_addr == INADDR_ANY) { 343 error = ENOTCONN; 344 break; 345 } 346 } 347 error = udp_output(inp, m); 348 m = NULL; 349 if (nam) { 350 in_pcbdisconnect(inp); 351 inp->inp_laddr = laddr; 352 splx(s); 353 } 354 } 355 break; 356 357 case PRU_ABORT: 358 soisdisconnected(so); 359 in_pcbdetach(inp); 360 break; 361 362 case PRU_SOCKADDR: 363 in_setsockaddr(inp, nam); 364 break; 365 366 case PRU_PEERADDR: 367 in_setpeeraddr(inp, nam); 368 break; 369 370 case PRU_SENSE: 371 /* 372 * stat: don't bother with a blocksize. 373 */ 374 return (0); 375 376 case PRU_SENDOOB: 377 case PRU_FASTTIMO: 378 case PRU_SLOWTIMO: 379 case PRU_PROTORCV: 380 case PRU_PROTOSEND: 381 error = EOPNOTSUPP; 382 break; 383 384 case PRU_RCVD: 385 case PRU_RCVOOB: 386 return (EOPNOTSUPP); /* do not free mbuf's */ 387 388 default: 389 panic("udp_usrreq"); 390 } 391 release: 392 if (m != NULL) 393 m_freem(m); 394 return (error); 395 } 396