1 /* $NetBSD: raw_ip.c,v 1.42 1999/01/30 21:43:16 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1982, 1986, 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)raw_ip.c 8.7 (Berkeley) 5/15/95 36 */ 37 38 #include "opt_mrouting.h" 39 40 #include <sys/param.h> 41 #include <sys/malloc.h> 42 #include <sys/mbuf.h> 43 #include <sys/socket.h> 44 #include <sys/protosw.h> 45 #include <sys/socketvar.h> 46 #include <sys/errno.h> 47 #include <sys/systm.h> 48 #include <sys/proc.h> 49 50 #include <net/if.h> 51 #include <net/route.h> 52 53 #include <netinet/in.h> 54 #include <netinet/in_systm.h> 55 #include <netinet/ip.h> 56 #include <netinet/ip_var.h> 57 #include <netinet/ip_mroute.h> 58 #include <netinet/in_pcb.h> 59 #include <netinet/in_var.h> 60 61 #include <machine/stdarg.h> 62 63 struct inpcbtable rawcbtable; 64 65 int rip_bind __P((struct inpcb *, struct mbuf *)); 66 int rip_connect __P((struct inpcb *, struct mbuf *)); 67 void rip_disconnect __P((struct inpcb *)); 68 69 /* 70 * Nominal space allocated to a raw ip socket. 71 */ 72 #define RIPSNDQ 8192 73 #define RIPRCVQ 8192 74 75 /* 76 * Raw interface to IP protocol. 77 */ 78 79 /* 80 * Initialize raw connection block q. 81 */ 82 void 83 rip_init() 84 { 85 86 in_pcbinit(&rawcbtable, 1, 1); 87 } 88 89 /* 90 * Setup generic address and protocol structures 91 * for raw_input routine, then pass them along with 92 * mbuf chain. 93 */ 94 void 95 #if __STDC__ 96 rip_input(struct mbuf *m, ...) 97 #else 98 rip_input(m, va_alist) 99 struct mbuf *m; 100 va_dcl 101 #endif 102 { 103 register struct ip *ip = mtod(m, struct ip *); 104 register struct inpcb *inp; 105 struct inpcb *last = 0; 106 struct mbuf *opts = 0; 107 struct sockaddr_in ripsrc; 108 109 ripsrc.sin_family = AF_INET; 110 ripsrc.sin_len = sizeof(struct sockaddr_in); 111 ripsrc.sin_addr = ip->ip_src; 112 ripsrc.sin_port = 0; 113 bzero((caddr_t)ripsrc.sin_zero, sizeof(ripsrc.sin_zero)); 114 115 /* 116 * XXX Compatibility: programs using raw IP expect ip_len 117 * XXX to have the header length subtracted. 118 */ 119 ip->ip_len -= ip->ip_hl << 2; 120 121 for (inp = rawcbtable.inpt_queue.cqh_first; 122 inp != (struct inpcb *)&rawcbtable.inpt_queue; 123 inp = inp->inp_queue.cqe_next) { 124 if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p) 125 continue; 126 if (!in_nullhost(inp->inp_laddr) && 127 !in_hosteq(inp->inp_laddr, ip->ip_dst)) 128 continue; 129 if (!in_nullhost(inp->inp_faddr) && 130 !in_hosteq(inp->inp_faddr, ip->ip_src)) 131 continue; 132 if (last) { 133 struct mbuf *n; 134 if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { 135 if (last->inp_flags & INP_CONTROLOPTS || 136 last->inp_socket->so_options & SO_TIMESTAMP) 137 ip_savecontrol(last, &opts, ip, n); 138 if (sbappendaddr(&last->inp_socket->so_rcv, 139 sintosa(&ripsrc), n, opts) == 0) { 140 /* should notify about lost packet */ 141 m_freem(n); 142 if (opts) 143 m_freem(opts); 144 } else 145 sorwakeup(last->inp_socket); 146 opts = NULL; 147 } 148 } 149 last = inp; 150 } 151 if (last) { 152 if (last->inp_flags & INP_CONTROLOPTS || 153 last->inp_socket->so_options & SO_TIMESTAMP) 154 ip_savecontrol(last, &opts, ip, m); 155 if (sbappendaddr(&last->inp_socket->so_rcv, 156 sintosa(&ripsrc), m, opts) == 0) { 157 m_freem(m); 158 if (opts) 159 m_freem(opts); 160 } else 161 sorwakeup(last->inp_socket); 162 } else { 163 m_freem(m); 164 ipstat.ips_noproto++; 165 ipstat.ips_delivered--; 166 } 167 } 168 169 /* 170 * Generate IP header and pass packet to ip_output. 171 * Tack on options user may have setup with control call. 172 */ 173 int 174 #if __STDC__ 175 rip_output(struct mbuf *m, ...) 176 #else 177 rip_output(m, va_alist) 178 struct mbuf *m; 179 va_dcl 180 #endif 181 { 182 register struct inpcb *inp; 183 register struct ip *ip; 184 struct mbuf *opts; 185 int flags; 186 va_list ap; 187 188 va_start(ap, m); 189 inp = va_arg(ap, struct inpcb *); 190 va_end(ap); 191 192 flags = 193 (inp->inp_socket->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST 194 | IP_RETURNMTU; 195 196 /* 197 * If the user handed us a complete IP packet, use it. 198 * Otherwise, allocate an mbuf for a header and fill it in. 199 */ 200 if ((inp->inp_flags & INP_HDRINCL) == 0) { 201 if ((m->m_pkthdr.len + sizeof(struct ip)) > IP_MAXPACKET) { 202 m_freem(m); 203 return (EMSGSIZE); 204 } 205 M_PREPEND(m, sizeof(struct ip), M_WAIT); 206 ip = mtod(m, struct ip *); 207 ip->ip_tos = 0; 208 ip->ip_off = 0; 209 ip->ip_p = inp->inp_ip.ip_p; 210 ip->ip_len = m->m_pkthdr.len; 211 ip->ip_src = inp->inp_laddr; 212 ip->ip_dst = inp->inp_faddr; 213 ip->ip_ttl = MAXTTL; 214 opts = inp->inp_options; 215 } else { 216 if (m->m_pkthdr.len > IP_MAXPACKET) { 217 m_freem(m); 218 return (EMSGSIZE); 219 } 220 ip = mtod(m, struct ip *); 221 if (m->m_pkthdr.len != ip->ip_len) { 222 m_freem(m); 223 return (EINVAL); 224 } 225 if (ip->ip_id == 0) 226 ip->ip_id = htons(ip_id++); 227 opts = NULL; 228 /* XXX prevent ip_output from overwriting header fields */ 229 flags |= IP_RAWOUTPUT; 230 ipstat.ips_rawout++; 231 } 232 return (ip_output(m, opts, &inp->inp_route, flags, inp->inp_moptions, &inp->inp_errormtu)); 233 } 234 235 /* 236 * Raw IP socket option processing. 237 */ 238 int 239 rip_ctloutput(op, so, level, optname, m) 240 int op; 241 struct socket *so; 242 int level, optname; 243 struct mbuf **m; 244 { 245 register struct inpcb *inp = sotoinpcb(so); 246 int error = 0; 247 248 if (level != IPPROTO_IP) { 249 error = ENOPROTOOPT; 250 if (op == PRCO_SETOPT && *m != 0) 251 (void) m_free(*m); 252 } else switch (op) { 253 254 case PRCO_SETOPT: 255 switch (optname) { 256 case IP_HDRINCL: 257 if (*m == 0 || (*m)->m_len < sizeof (int)) 258 error = EINVAL; 259 else { 260 if (*mtod(*m, int *)) 261 inp->inp_flags |= INP_HDRINCL; 262 else 263 inp->inp_flags &= ~INP_HDRINCL; 264 } 265 if (*m != 0) 266 (void) m_free(*m); 267 break; 268 269 #ifdef MROUTING 270 case MRT_INIT: 271 case MRT_DONE: 272 case MRT_ADD_VIF: 273 case MRT_DEL_VIF: 274 case MRT_ADD_MFC: 275 case MRT_DEL_MFC: 276 case MRT_ASSERT: 277 error = ip_mrouter_set(so, optname, m); 278 break; 279 #endif 280 281 default: 282 error = ip_ctloutput(op, so, level, optname, m); 283 break; 284 } 285 break; 286 287 case PRCO_GETOPT: 288 switch (optname) { 289 case IP_HDRINCL: 290 *m = m_get(M_WAIT, M_SOOPTS); 291 (*m)->m_len = sizeof (int); 292 *mtod(*m, int *) = inp->inp_flags & INP_HDRINCL ? 1 : 0; 293 break; 294 295 #ifdef MROUTING 296 case MRT_VERSION: 297 case MRT_ASSERT: 298 error = ip_mrouter_get(so, optname, m); 299 break; 300 #endif 301 302 default: 303 error = ip_ctloutput(op, so, level, optname, m); 304 break; 305 } 306 break; 307 } 308 return (error); 309 } 310 311 int 312 rip_bind(inp, nam) 313 struct inpcb *inp; 314 struct mbuf *nam; 315 { 316 struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); 317 318 if (nam->m_len != sizeof(*addr)) 319 return (EINVAL); 320 if (ifnet.tqh_first == 0) 321 return (EADDRNOTAVAIL); 322 if (addr->sin_family != AF_INET && 323 addr->sin_family != AF_IMPLINK) 324 return (EAFNOSUPPORT); 325 if (!in_nullhost(addr->sin_addr) && 326 ifa_ifwithaddr(sintosa(addr)) == 0) 327 return (EADDRNOTAVAIL); 328 inp->inp_laddr = addr->sin_addr; 329 return (0); 330 } 331 332 int 333 rip_connect(inp, nam) 334 struct inpcb *inp; 335 struct mbuf *nam; 336 { 337 struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *); 338 339 if (nam->m_len != sizeof(*addr)) 340 return (EINVAL); 341 if (ifnet.tqh_first == 0) 342 return (EADDRNOTAVAIL); 343 if (addr->sin_family != AF_INET && 344 addr->sin_family != AF_IMPLINK) 345 return (EAFNOSUPPORT); 346 inp->inp_faddr = addr->sin_addr; 347 return (0); 348 } 349 350 void 351 rip_disconnect(inp) 352 struct inpcb *inp; 353 { 354 355 inp->inp_faddr = zeroin_addr; 356 } 357 358 u_long rip_sendspace = RIPSNDQ; 359 u_long rip_recvspace = RIPRCVQ; 360 361 /*ARGSUSED*/ 362 int 363 rip_usrreq(so, req, m, nam, control, p) 364 register struct socket *so; 365 int req; 366 struct mbuf *m, *nam, *control; 367 struct proc *p; 368 { 369 register struct inpcb *inp; 370 int s; 371 register int error = 0; 372 #ifdef MROUTING 373 extern struct socket *ip_mrouter; 374 #endif 375 376 if (req == PRU_CONTROL) 377 return (in_control(so, (long)m, (caddr_t)nam, 378 (struct ifnet *)control, p)); 379 380 s = splsoftnet(); 381 inp = sotoinpcb(so); 382 #ifdef DIAGNOSTIC 383 if (req != PRU_SEND && req != PRU_SENDOOB && control) 384 panic("rip_usrreq: unexpected control mbuf"); 385 #endif 386 if (inp == 0 && req != PRU_ATTACH) { 387 error = EINVAL; 388 goto release; 389 } 390 391 switch (req) { 392 393 case PRU_ATTACH: 394 if (inp != 0) { 395 error = EISCONN; 396 break; 397 } 398 if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag))) { 399 error = EACCES; 400 break; 401 } 402 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 403 error = soreserve(so, rip_sendspace, rip_recvspace); 404 if (error) 405 break; 406 } 407 error = in_pcballoc(so, &rawcbtable); 408 if (error) 409 break; 410 inp = sotoinpcb(so); 411 inp->inp_ip.ip_p = (long)nam; 412 break; 413 414 case PRU_DETACH: 415 #ifdef MROUTING 416 if (so == ip_mrouter) 417 ip_mrouter_done(); 418 #endif 419 in_pcbdetach(inp); 420 break; 421 422 case PRU_BIND: 423 error = rip_bind(inp, nam); 424 break; 425 426 case PRU_LISTEN: 427 error = EOPNOTSUPP; 428 break; 429 430 case PRU_CONNECT: 431 error = rip_connect(inp, nam); 432 if (error) 433 break; 434 soisconnected(so); 435 break; 436 437 case PRU_CONNECT2: 438 error = EOPNOTSUPP; 439 break; 440 441 case PRU_DISCONNECT: 442 soisdisconnected(so); 443 rip_disconnect(inp); 444 break; 445 446 /* 447 * Mark the connection as being incapable of further input. 448 */ 449 case PRU_SHUTDOWN: 450 socantsendmore(so); 451 break; 452 453 case PRU_RCVD: 454 error = EOPNOTSUPP; 455 break; 456 457 /* 458 * Ship a packet out. The appropriate raw output 459 * routine handles any massaging necessary. 460 */ 461 case PRU_SEND: 462 if (control && control->m_len) { 463 m_freem(control); 464 m_freem(m); 465 error = EINVAL; 466 break; 467 } 468 { 469 if (nam) { 470 if ((so->so_state & SS_ISCONNECTED) != 0) { 471 error = EISCONN; 472 goto die; 473 } 474 error = rip_connect(inp, nam); 475 if (error) { 476 die: 477 m_freem(m); 478 break; 479 } 480 } else { 481 if ((so->so_state & SS_ISCONNECTED) == 0) { 482 error = ENOTCONN; 483 goto die; 484 } 485 } 486 error = rip_output(m, inp); 487 if (nam) 488 rip_disconnect(inp); 489 } 490 break; 491 492 case PRU_SENSE: 493 /* 494 * stat: don't bother with a blocksize. 495 */ 496 splx(s); 497 return (0); 498 499 case PRU_RCVOOB: 500 error = EOPNOTSUPP; 501 break; 502 503 case PRU_SENDOOB: 504 m_freem(control); 505 m_freem(m); 506 error = EOPNOTSUPP; 507 break; 508 509 case PRU_SOCKADDR: 510 in_setsockaddr(inp, nam); 511 break; 512 513 case PRU_PEERADDR: 514 in_setpeeraddr(inp, nam); 515 break; 516 517 default: 518 panic("rip_usrreq"); 519 } 520 521 release: 522 splx(s); 523 return (error); 524 } 525