ip_divert.c revision 67893
1336815Sdim/* 2336815Sdim * Copyright (c) 1982, 1986, 1988, 1993 3336815Sdim * The Regents of the University of California. All rights reserved. 4336815Sdim * 5336815Sdim * Redistribution and use in source and binary forms, with or without 6336815Sdim * modification, are permitted provided that the following conditions 7336815Sdim * are met: 8336815Sdim * 1. Redistributions of source code must retain the above copyright 9336815Sdim * notice, this list of conditions and the following disclaimer. 10336815Sdim * 2. Redistributions in binary form must reproduce the above copyright 11336815Sdim * notice, this list of conditions and the following disclaimer in the 12336815Sdim * documentation and/or other materials provided with the distribution. 13336815Sdim * 3. All advertising materials mentioning features or use of this software 14336815Sdim * must display the following acknowledgement: 15336815Sdim * This product includes software developed by the University of 16336815Sdim * California, Berkeley and its contributors. 17336815Sdim * 4. Neither the name of the University nor the names of its contributors 18336815Sdim * may be used to endorse or promote products derived from this software 19336815Sdim * without specific prior written permission. 20336815Sdim * 21336815Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22336815Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23336815Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24336815Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25336815Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26336815Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27336815Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28336815Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29336815Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30336815Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31336815Sdim * SUCH DAMAGE. 32336815Sdim * 33336815Sdim * $FreeBSD: head/sys/netinet/ip_divert.c 67893 2000-10-29 16:06:56Z phk $ 34336815Sdim */ 35336815Sdim 36336815Sdim#include "opt_inet.h" 37336815Sdim#include "opt_ipfw.h" 38336815Sdim#include "opt_ipdivert.h" 39336815Sdim#include "opt_ipsec.h" 40336815Sdim 41336815Sdim#ifndef INET 42336815Sdim#error "IPDIVERT requires INET." 43336815Sdim#endif 44336815Sdim 45336815Sdim#include <sys/param.h> 46336815Sdim#include <sys/kernel.h> 47336815Sdim#include <sys/malloc.h> 48336815Sdim#include <sys/mbuf.h> 49336815Sdim#include <sys/socket.h> 50336815Sdim#include <sys/protosw.h> 51336815Sdim#include <sys/socketvar.h> 52336815Sdim#include <sys/sysctl.h> 53336815Sdim#include <sys/systm.h> 54336815Sdim 55336815Sdim#include <vm/vm_zone.h> 56336815Sdim 57336815Sdim#include <net/if.h> 58336815Sdim#include <net/route.h> 59336815Sdim 60336815Sdim#include <netinet/in.h> 61336815Sdim#include <netinet/in_systm.h> 62336815Sdim#include <netinet/ip.h> 63336815Sdim#include <netinet/in_pcb.h> 64336815Sdim#include <netinet/in_var.h> 65336815Sdim#include <netinet/ip_var.h> 66336815Sdim 67336815Sdim/* 68336815Sdim * Divert sockets 69336815Sdim */ 70336815Sdim 71336815Sdim/* 72336815Sdim * Allocate enough space to hold a full IP packet 73336815Sdim */ 74336815Sdim#define DIVSNDQ (65536 + 100) 75336815Sdim#define DIVRCVQ (65536 + 100) 76336815Sdim 77336815Sdim/* 78336815Sdim * A 16 bit cookie is passed to and from the user process. 79336815Sdim * The user process can send it back to help the caller know 80336815Sdim * something about where the packet originally came from. 81336815Sdim * 82336815Sdim * In the case of ipfw, then the cookie is the rule that sent 83336815Sdim * us here. On reinjection is is the rule after which processing 84336815Sdim * should continue. Leaving it the same will make processing start 85336815Sdim * at the rule number after that which sent it here. Setting it to 86336815Sdim * 0 will restart processing at the beginning. 87336815Sdim * 88336815Sdim * For divert_packet(), ip_divert_cookie is an input value only. 89336815Sdim * For div_output(), ip_divert_cookie is an output value only. 90336815Sdim */ 91336815Sdimu_int16_t ip_divert_cookie; 92336815Sdim 93336815Sdim/* Internal variables */ 94336815Sdimstatic struct inpcbhead divcb; 95336815Sdimstatic struct inpcbinfo divcbinfo; 96336815Sdim 97336815Sdimstatic u_long div_sendspace = DIVSNDQ; /* XXX sysctl ? */ 98336815Sdimstatic u_long div_recvspace = DIVRCVQ; /* XXX sysctl ? */ 99336815Sdim 100336815Sdim/* Optimization: have this preinitialized */ 101336815Sdimstatic struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET }; 102336815Sdim 103336815Sdim/* Internal functions */ 104336815Sdimstatic int div_output(struct socket *so, 105336815Sdim struct mbuf *m, struct sockaddr *addr, struct mbuf *control); 106336815Sdim 107336815Sdim/* 108336815Sdim * Initialize divert connection block queue. 109336815Sdim */ 110336815Sdimvoid 111336815Sdimdiv_init(void) 112336815Sdim{ 113336815Sdim LIST_INIT(&divcb); 114336815Sdim divcbinfo.listhead = &divcb; 115336815Sdim /* 116336815Sdim * XXX We don't use the hash list for divert IP, but it's easier 117336815Sdim * to allocate a one entry hash list than it is to check all 118336815Sdim * over the place for hashbase == NULL. 119336815Sdim */ 120336815Sdim divcbinfo.hashbase = hashinit(1, M_PCB, &divcbinfo.hashmask); 121336815Sdim divcbinfo.porthashbase = hashinit(1, M_PCB, &divcbinfo.porthashmask); 122336815Sdim divcbinfo.ipi_zone = zinit("divcb", sizeof(struct inpcb), 123336815Sdim maxsockets, ZONE_INTERRUPT, 0); 124336815Sdim} 125336815Sdim 126336815Sdim/* 127336815Sdim * IPPROTO_DIVERT is not a real IP protocol; don't allow any packets 128336815Sdim * with that protocol number to enter the system from the outside. 129336815Sdim */ 130336815Sdimvoid 131336815Sdimdiv_input(struct mbuf *m, int off, int proto) 132336815Sdim{ 133336815Sdim ipstat.ips_noproto++; 134336815Sdim m_freem(m); 135336815Sdim} 136336815Sdim 137336815Sdim/* 138336815Sdim * Divert a packet by passing it up to the divert socket at port 'port'. 139336815Sdim * 140336815Sdim * Setup generic address and protocol structures for div_input routine, 141336815Sdim * then pass them along with mbuf chain. 142336815Sdim */ 143336815Sdimvoid 144336815Sdimdivert_packet(struct mbuf *m, int incoming, int port) 145336815Sdim{ 146336815Sdim struct ip *ip; 147336815Sdim struct inpcb *inp; 148336815Sdim struct socket *sa; 149336815Sdim u_int16_t nport; 150336815Sdim 151336815Sdim /* Sanity check */ 152336815Sdim KASSERT(port != 0, ("%s: port=0", __FUNCTION__)); 153336815Sdim 154336815Sdim /* Record and reset divert cookie */ 155336815Sdim divsrc.sin_port = ip_divert_cookie; 156336815Sdim ip_divert_cookie = 0; 157336815Sdim 158336815Sdim /* Assure header */ 159336815Sdim if (m->m_len < sizeof(struct ip) && 160336815Sdim (m = m_pullup(m, sizeof(struct ip))) == 0) { 161336815Sdim return; 162336815Sdim } 163336815Sdim ip = mtod(m, struct ip *); 164336815Sdim 165336815Sdim /* 166336815Sdim * Record receive interface address, if any. 167336815Sdim * But only for incoming packets. 168336815Sdim */ 169336815Sdim divsrc.sin_addr.s_addr = 0; 170336815Sdim if (incoming) { 171336815Sdim struct ifaddr *ifa; 172336815Sdim 173336815Sdim /* Sanity check */ 174336815Sdim KASSERT((m->m_flags & M_PKTHDR), ("%s: !PKTHDR", __FUNCTION__)); 175336815Sdim 176336815Sdim /* Find IP address for receive interface */ 177336815Sdim for (ifa = m->m_pkthdr.rcvif->if_addrhead.tqh_first; 178336815Sdim ifa != NULL; ifa = ifa->ifa_link.tqe_next) { 179336815Sdim if (ifa->ifa_addr == NULL) 180336815Sdim continue; 181336815Sdim if (ifa->ifa_addr->sa_family != AF_INET) 182336815Sdim continue; 183336815Sdim divsrc.sin_addr = 184336815Sdim ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; 185336815Sdim break; 186336815Sdim } 187336815Sdim } 188336815Sdim /* 189336815Sdim * Record the incoming interface name whenever we have one. 190336815Sdim */ 191336815Sdim bzero(&divsrc.sin_zero, sizeof(divsrc.sin_zero)); 192336815Sdim if (m->m_pkthdr.rcvif) { 193336815Sdim /* 194336815Sdim * Hide the actual interface name in there in the 195336815Sdim * sin_zero array. XXX This needs to be moved to a 196336815Sdim * different sockaddr type for divert, e.g. 197336815Sdim * sockaddr_div with multiple fields like 198336815Sdim * sockaddr_dl. Presently we have only 7 bytes 199336815Sdim * but that will do for now as most interfaces 200336815Sdim * are 4 or less + 2 or less bytes for unit. 201336815Sdim * There is probably a faster way of doing this, 202336815Sdim * possibly taking it from the sockaddr_dl on the iface. 203336815Sdim * This solves the problem of a P2P link and a LAN interface 204336815Sdim * having the same address, which can result in the wrong 205336815Sdim * interface being assigned to the packet when fed back 206336815Sdim * into the divert socket. Theoretically if the daemon saves 207336815Sdim * and re-uses the sockaddr_in as suggested in the man pages, 208336815Sdim * this iface name will come along for the ride. 209336815Sdim * (see div_output for the other half of this.) 210336815Sdim */ 211336815Sdim snprintf(divsrc.sin_zero, sizeof(divsrc.sin_zero), 212336815Sdim "%s%d", m->m_pkthdr.rcvif->if_name, 213336815Sdim m->m_pkthdr.rcvif->if_unit); 214336815Sdim } 215336815Sdim 216336815Sdim /* Put packet on socket queue, if any */ 217336815Sdim sa = NULL; 218336815Sdim nport = htons((u_int16_t)port); 219336815Sdim for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) { 220336815Sdim if (inp->inp_lport == nport) 221336815Sdim sa = inp->inp_socket; 222336815Sdim } 223336815Sdim if (sa) { 224336815Sdim if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc, 225336815Sdim m, (struct mbuf *)0) == 0) 226336815Sdim m_freem(m); 227336815Sdim else 228336815Sdim sorwakeup(sa); 229336815Sdim } else { 230336815Sdim m_freem(m); 231336815Sdim ipstat.ips_noproto++; 232336815Sdim ipstat.ips_delivered--; 233336815Sdim } 234336815Sdim} 235336815Sdim 236336815Sdim/* 237336815Sdim * Deliver packet back into the IP processing machinery. 238336815Sdim * 239336815Sdim * If no address specified, or address is 0.0.0.0, send to ip_output(); 240336815Sdim * otherwise, send to ip_input() and mark as having been received on 241336815Sdim * the interface with that address. 242336815Sdim */ 243336815Sdimstatic int 244336815Sdimdiv_output(so, m, addr, control) 245336815Sdim struct socket *so; 246336815Sdim register struct mbuf *m; 247336815Sdim struct sockaddr *addr; 248336815Sdim struct mbuf *control; 249336815Sdim{ 250336815Sdim register struct inpcb *const inp = sotoinpcb(so); 251336815Sdim register struct ip *const ip = mtod(m, struct ip *); 252336815Sdim struct sockaddr_in *sin = (struct sockaddr_in *)addr; 253336815Sdim int error = 0; 254336815Sdim 255336815Sdim if (control) 256336815Sdim m_freem(control); /* XXX */ 257336815Sdim 258336815Sdim /* Loopback avoidance and state recovery */ 259336815Sdim if (sin) { 260336815Sdim int len = 0; 261336815Sdim char *c = sin->sin_zero; 262336815Sdim 263336815Sdim ip_divert_cookie = sin->sin_port; 264336815Sdim 265336815Sdim /* 266336815Sdim * Find receive interface with the given name or IP address. 267336815Sdim * The name is user supplied data so don't trust it's size or 268336815Sdim * that it is zero terminated. The name has priority. 269336815Sdim * We are presently assuming that the sockaddr_in 270336815Sdim * has not been replaced by a sockaddr_div, so we limit it 271336815Sdim * to 16 bytes in total. the name is stuffed (if it exists) 272336815Sdim * in the sin_zero[] field. 273336815Sdim */ 274336815Sdim while (*c++ && (len++ < sizeof(sin->sin_zero))); 275336815Sdim if ((len > 0) && (len < sizeof(sin->sin_zero))) 276336815Sdim m->m_pkthdr.rcvif = ifunit(sin->sin_zero); 277336815Sdim } else { 278336815Sdim ip_divert_cookie = 0; 279336815Sdim } 280336815Sdim 281336815Sdim /* Reinject packet into the system as incoming or outgoing */ 282336815Sdim if (!sin || sin->sin_addr.s_addr == 0) { 283336815Sdim /* 284336815Sdim * Don't allow both user specified and setsockopt options, 285336815Sdim * and don't allow packet length sizes that will crash 286336815Sdim */ 287336815Sdim if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) || 288336815Sdim ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) { 289336815Sdim error = EINVAL; 290336815Sdim goto cantsend; 291336815Sdim } 292336815Sdim 293336815Sdim /* Convert fields to host order for ip_output() */ 294336815Sdim NTOHS(ip->ip_len); 295336815Sdim NTOHS(ip->ip_off); 296336815Sdim 297336815Sdim /* Send packet to output processing */ 298336815Sdim ipstat.ips_rawout++; /* XXX */ 299336815Sdim error = ip_output(m, inp->inp_options, &inp->inp_route, 300336815Sdim (so->so_options & SO_DONTROUTE) | 301336815Sdim IP_ALLOWBROADCAST | IP_RAWOUTPUT, 302336815Sdim inp->inp_moptions); 303336815Sdim } else { 304336815Sdim struct ifaddr *ifa; 305336815Sdim 306336815Sdim /* If no luck with the name above. check by IP address. */ 307336815Sdim if (m->m_pkthdr.rcvif == NULL) { 308336815Sdim /* 309336815Sdim * Make sure there are no distractions 310336815Sdim * for ifa_ifwithaddr. Clear the port and the ifname. 311336815Sdim * Maybe zap all 8 bytes at once using a 64bit write? 312336815Sdim */ 313336815Sdim bzero(sin->sin_zero, sizeof(sin->sin_zero)); 314336815Sdim /* *((u_int64_t *)sin->sin_zero) = 0; */ /* XXX ?? */ 315336815Sdim sin->sin_port = 0; 316336815Sdim if (!(ifa = ifa_ifwithaddr((struct sockaddr *) sin))) { 317336815Sdim error = EADDRNOTAVAIL; 318336815Sdim goto cantsend; 319336815Sdim } 320336815Sdim m->m_pkthdr.rcvif = ifa->ifa_ifp; 321336815Sdim } 322336815Sdim 323336815Sdim /* Send packet to input processing */ 324336815Sdim ip_input(m); 325336815Sdim } 326336815Sdim 327336815Sdim /* paranoid: Reset for next time (and other packets) */ 328336815Sdim /* almost definitly already done in the ipfw filter but.. */ 329336815Sdim ip_divert_cookie = 0; 330336815Sdim return error; 331336815Sdim 332336815Sdimcantsend: 333336815Sdim m_freem(m); 334336815Sdim ip_divert_cookie = 0; 335336815Sdim return error; 336336815Sdim} 337336815Sdim 338336815Sdimstatic int 339336815Sdimdiv_attach(struct socket *so, int proto, struct proc *p) 340336815Sdim{ 341336815Sdim struct inpcb *inp; 342336815Sdim int error, s; 343336815Sdim 344336815Sdim inp = sotoinpcb(so); 345336815Sdim if (inp) 346336815Sdim panic("div_attach"); 347336815Sdim if (p && (error = suser(p)) != 0) 348336815Sdim return error; 349336815Sdim 350336815Sdim error = soreserve(so, div_sendspace, div_recvspace); 351336815Sdim if (error) 352336815Sdim return error; 353336815Sdim s = splnet(); 354336815Sdim error = in_pcballoc(so, &divcbinfo, p); 355336815Sdim splx(s); 356336815Sdim if (error) 357336815Sdim return error; 358336815Sdim inp = (struct inpcb *)so->so_pcb; 359336815Sdim inp->inp_ip_p = proto; 360336815Sdim inp->inp_vflag |= INP_IPV4; 361336815Sdim inp->inp_flags |= INP_HDRINCL; 362336815Sdim /* The socket is always "connected" because 363336815Sdim we always know "where" to send the packet */ 364336815Sdim so->so_state |= SS_ISCONNECTED; 365336815Sdim#ifdef IPSEC 366336815Sdim error = ipsec_init_policy(so, &inp->inp_sp); 367336815Sdim if (error != 0) { 368336815Sdim in_pcbdetach(inp); 369336815Sdim return error; 370336815Sdim } 371336815Sdim#endif /*IPSEC*/ 372336815Sdim return 0; 373336815Sdim} 374336815Sdim 375336815Sdimstatic int 376336815Sdimdiv_detach(struct socket *so) 377336815Sdim{ 378336815Sdim struct inpcb *inp; 379336815Sdim 380336815Sdim inp = sotoinpcb(so); 381336815Sdim if (inp == 0) 382336815Sdim panic("div_detach"); 383336815Sdim in_pcbdetach(inp); 384336815Sdim return 0; 385336815Sdim} 386336815Sdim 387336815Sdimstatic int 388336815Sdimdiv_abort(struct socket *so) 389336815Sdim{ 390336815Sdim soisdisconnected(so); 391336815Sdim return div_detach(so); 392336815Sdim} 393336815Sdim 394336815Sdimstatic int 395336815Sdimdiv_disconnect(struct socket *so) 396336815Sdim{ 397336815Sdim if ((so->so_state & SS_ISCONNECTED) == 0) 398336815Sdim return ENOTCONN; 399336815Sdim return div_abort(so); 400336815Sdim} 401336815Sdim 402336815Sdimstatic int 403336815Sdimdiv_bind(struct socket *so, struct sockaddr *nam, struct proc *p) 404336815Sdim{ 405336815Sdim struct inpcb *inp; 406336815Sdim int s; 407336815Sdim int error; 408336815Sdim 409336815Sdim s = splnet(); 410336815Sdim inp = sotoinpcb(so); 411336815Sdim /* in_pcbbind assumes that the socket is a sockaddr_in 412336815Sdim * and in_pcbbind requires a valid address. Since divert 413336815Sdim * sockets don't we need to make sure the address is 414336815Sdim * filled in properly. 415336815Sdim * XXX -- divert should not be abusing in_pcbind 416336815Sdim * and should probably have its own family. 417336815Sdim */ 418336815Sdim if (nam->sa_family != AF_INET) { 419336815Sdim error = EAFNOSUPPORT; 420336815Sdim } else { 421336815Sdim ((struct sockaddr_in *)nam)->sin_addr.s_addr = INADDR_ANY; 422336815Sdim error = in_pcbbind(inp, nam, p); 423336815Sdim } 424336815Sdim splx(s); 425336815Sdim return error; 426336815Sdim} 427336815Sdim 428336815Sdimstatic int 429336815Sdimdiv_shutdown(struct socket *so) 430336815Sdim{ 431336815Sdim socantsendmore(so); 432336815Sdim return 0; 433336815Sdim} 434336815Sdim 435336815Sdimstatic int 436336815Sdimdiv_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 437336815Sdim struct mbuf *control, struct proc *p) 438336815Sdim{ 439336815Sdim /* Packet must have a header (but that's about it) */ 440336815Sdim if (m->m_len < sizeof (struct ip) && 441336815Sdim (m = m_pullup(m, sizeof (struct ip))) == 0) { 442336815Sdim ipstat.ips_toosmall++; 443336815Sdim m_freem(m); 444336815Sdim return EINVAL; 445336815Sdim } 446336815Sdim 447336815Sdim /* Send packet */ 448336815Sdim return div_output(so, m, nam, control); 449336815Sdim} 450336815Sdim 451336815Sdimstatic int 452336815Sdimdiv_pcblist(SYSCTL_HANDLER_ARGS) 453336815Sdim{ 454336815Sdim int error, i, n, s; 455336815Sdim struct inpcb *inp, **inp_list; 456336815Sdim inp_gen_t gencnt; 457336815Sdim struct xinpgen xig; 458336815Sdim 459336815Sdim /* 460336815Sdim * The process of preparing the TCB list is too time-consuming and 461336815Sdim * resource-intensive to repeat twice on every request. 462336815Sdim */ 463336815Sdim if (req->oldptr == 0) { 464336815Sdim n = divcbinfo.ipi_count; 465336815Sdim req->oldidx = 2 * (sizeof xig) 466336815Sdim + (n + n/8) * sizeof(struct xinpcb); 467 return 0; 468 } 469 470 if (req->newptr != 0) 471 return EPERM; 472 473 /* 474 * OK, now we're committed to doing something. 475 */ 476 s = splnet(); 477 gencnt = divcbinfo.ipi_gencnt; 478 n = divcbinfo.ipi_count; 479 splx(s); 480 481 xig.xig_len = sizeof xig; 482 xig.xig_count = n; 483 xig.xig_gen = gencnt; 484 xig.xig_sogen = so_gencnt; 485 error = SYSCTL_OUT(req, &xig, sizeof xig); 486 if (error) 487 return error; 488 489 inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK); 490 if (inp_list == 0) 491 return ENOMEM; 492 493 s = splnet(); 494 for (inp = divcbinfo.listhead->lh_first, i = 0; inp && i < n; 495 inp = inp->inp_list.le_next) { 496 if (inp->inp_gencnt <= gencnt && !prison_xinpcb(req->p, inp)) 497 inp_list[i++] = inp; 498 } 499 splx(s); 500 n = i; 501 502 error = 0; 503 for (i = 0; i < n; i++) { 504 inp = inp_list[i]; 505 if (inp->inp_gencnt <= gencnt) { 506 struct xinpcb xi; 507 xi.xi_len = sizeof xi; 508 /* XXX should avoid extra copy */ 509 bcopy(inp, &xi.xi_inp, sizeof *inp); 510 if (inp->inp_socket) 511 sotoxsocket(inp->inp_socket, &xi.xi_socket); 512 error = SYSCTL_OUT(req, &xi, sizeof xi); 513 } 514 } 515 if (!error) { 516 /* 517 * Give the user an updated idea of our state. 518 * If the generation differs from what we told 519 * her before, she knows that something happened 520 * while we were processing this request, and it 521 * might be necessary to retry. 522 */ 523 s = splnet(); 524 xig.xig_gen = divcbinfo.ipi_gencnt; 525 xig.xig_sogen = so_gencnt; 526 xig.xig_count = divcbinfo.ipi_count; 527 splx(s); 528 error = SYSCTL_OUT(req, &xig, sizeof xig); 529 } 530 free(inp_list, M_TEMP); 531 return error; 532} 533 534SYSCTL_DECL(_net_inet_divert); 535SYSCTL_PROC(_net_inet_divert, OID_AUTO, pcblist, CTLFLAG_RD, 0, 0, 536 div_pcblist, "S,xinpcb", "List of active divert sockets"); 537 538struct pr_usrreqs div_usrreqs = { 539 div_abort, pru_accept_notsupp, div_attach, div_bind, 540 pru_connect_notsupp, pru_connect2_notsupp, in_control, div_detach, 541 div_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp, 542 pru_rcvoob_notsupp, div_send, pru_sense_null, div_shutdown, 543 in_setsockaddr, sosend, soreceive, sopoll 544}; 545