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.9 (Berkeley) 10/12/88 18 */ 19 20 #include "param.h" 21 #include "dir.h" 22 #include "user.h" 23 #include "malloc.h" 24 #include "mbuf.h" 25 #include "protosw.h" 26 #include "socket.h" 27 #include "socketvar.h" 28 #include "errno.h" 29 #include "stat.h" 30 31 #include "../net/if.h" 32 #include "../net/route.h" 33 34 #include "in.h" 35 #include "in_pcb.h" 36 #include "in_systm.h" 37 #include "ip.h" 38 #include "ip_var.h" 39 #include "ip_icmp.h" 40 #include "udp.h" 41 #include "udp_var.h" 42 43 /* 44 * UDP protocol implementation. 45 * Per RFC 768, August, 1980. 46 */ 47 udp_init() 48 { 49 50 udb.inp_next = udb.inp_prev = &udb; 51 } 52 53 #ifndef COMPAT_42 54 int udpcksum = 1; 55 #else 56 int udpcksum = 0; /* XXX */ 57 #endif 58 int udp_ttl = UDP_TTL; 59 60 struct sockaddr_in udp_in = { AF_INET }; 61 62 udp_input(m, iphlen) 63 register struct mbuf *m; 64 int iphlen; 65 { 66 register struct udpiphdr *ui; 67 register struct inpcb *inp; 68 int len; 69 struct ip ip; 70 71 /* 72 * Get IP and UDP header together in first mbuf. 73 * Note: IP leaves IP header in first mbuf. 74 */ 75 ui = mtod(m, struct udpiphdr *); 76 if (iphlen > sizeof (struct ip)) 77 ip_stripoptions(m, (struct mbuf *)0); 78 if (m->m_len < sizeof (struct udpiphdr)) { 79 if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) { 80 udpstat.udps_hdrops++; 81 return; 82 } 83 ui = mtod(m, struct udpiphdr *); 84 } 85 86 /* 87 * Make mbuf data length reflect UDP length. 88 * If not enough data to reflect UDP length, drop. 89 */ 90 len = ntohs((u_short)ui->ui_ulen); 91 if (((struct ip *)ui)->ip_len != len) { 92 if (len > ((struct ip *)ui)->ip_len) { 93 udpstat.udps_badlen++; 94 goto bad; 95 } 96 m_adj(m, len - ((struct ip *)ui)->ip_len); 97 /* ((struct ip *)ui)->ip_len = len; */ 98 } 99 /* 100 * Save a copy of the IP header in case we want restore it for ICMP. 101 */ 102 ip = *(struct ip *)ui; 103 104 /* 105 * Checksum extended UDP header and data. 106 */ 107 if (udpcksum && ui->ui_sum) { 108 ui->ui_next = ui->ui_prev = 0; 109 ui->ui_x1 = 0; 110 ui->ui_len = ui->ui_ulen; 111 if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) { 112 udpstat.udps_badsum++; 113 m_freem(m); 114 return; 115 } 116 } 117 118 /* 119 * Locate pcb for datagram. 120 */ 121 inp = in_pcblookup(&udb, 122 ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport, 123 INPLOOKUP_WILDCARD); 124 if (inp == 0) { 125 /* don't send ICMP response for broadcast packet */ 126 if (m->m_flags & M_BCAST) 127 goto bad; 128 *(struct ip *)ui = ip; 129 icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT); 130 return; 131 } 132 133 /* 134 * Construct sockaddr format source address. 135 * Stuff source address and datagram in user buffer. 136 */ 137 udp_in.sin_port = ui->ui_sport; 138 udp_in.sin_addr = ui->ui_src; 139 m->m_len -= sizeof (struct udpiphdr); 140 m->m_pkthdr.len -= sizeof (struct udpiphdr); 141 m->m_data += sizeof (struct udpiphdr); 142 if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, 143 m, (struct mbuf *)0) == 0) 144 goto bad; 145 sorwakeup(inp->inp_socket); 146 return; 147 bad: 148 m_freem(m); 149 } 150 151 /* 152 * Notify a udp user of an asynchronous error; 153 * just wake up so that he can collect error status. 154 */ 155 udp_notify(inp) 156 register struct inpcb *inp; 157 { 158 159 sorwakeup(inp->inp_socket); 160 sowwakeup(inp->inp_socket); 161 } 162 163 udp_ctlinput(cmd, sa) 164 int cmd; 165 struct sockaddr *sa; 166 { 167 extern u_char inetctlerrmap[]; 168 struct sockaddr_in *sin; 169 int in_rtchange(); 170 171 if ((unsigned)cmd > PRC_NCMDS) 172 return; 173 if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) 174 return; 175 sin = (struct sockaddr_in *)sa; 176 if (sin->sin_addr.s_addr == INADDR_ANY) 177 return; 178 179 switch (cmd) { 180 181 case PRC_QUENCH: 182 break; 183 184 case PRC_ROUTEDEAD: 185 case PRC_REDIRECT_NET: 186 case PRC_REDIRECT_HOST: 187 case PRC_REDIRECT_TOSNET: 188 case PRC_REDIRECT_TOSHOST: 189 in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange); 190 break; 191 192 default: 193 if (inetctlerrmap[cmd] == 0) 194 return; /* XXX */ 195 in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd], 196 udp_notify); 197 } 198 } 199 200 udp_output(inp, m) 201 register struct inpcb *inp; 202 register struct mbuf *m; 203 { 204 register struct udpiphdr *ui; 205 register int len = m->m_pkthdr.len; 206 207 /* 208 * Calculate data length and get a mbuf 209 * for UDP and IP headers. 210 */ 211 M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT); 212 213 /* 214 * Fill in mbuf with extended UDP header 215 * and addresses and length put into network format. 216 */ 217 ui = mtod(m, struct udpiphdr *); 218 ui->ui_next = ui->ui_prev = 0; 219 ui->ui_x1 = 0; 220 ui->ui_pr = IPPROTO_UDP; 221 ui->ui_len = htons((u_short)len + sizeof (struct udphdr)); 222 ui->ui_src = inp->inp_laddr; 223 ui->ui_dst = inp->inp_faddr; 224 ui->ui_sport = inp->inp_lport; 225 ui->ui_dport = inp->inp_fport; 226 ui->ui_ulen = ui->ui_len; 227 228 /* 229 * Stuff checksum and output datagram. 230 */ 231 ui->ui_sum = 0; 232 if (udpcksum) { 233 if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0) 234 ui->ui_sum = 0xffff; 235 } 236 ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; 237 ((struct ip *)ui)->ip_ttl = udp_ttl; 238 return (ip_output(m, inp->inp_options, &inp->inp_route, 239 inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); 240 } 241 242 u_long udp_sendspace = 2048; /* really max datagram size */ 243 u_long udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */ 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