-static void in_pcb_detach_port( struct inpcb *inp);
-int
-in_pcb_grab_port (struct inpcbinfo *pcbinfo,
- u_short options,
- struct in_addr laddr,
- u_short *lport,
- struct in_addr faddr,
- u_short fport,
- u_int cookie,
- u_char owner_id)
-{
- struct inpcb *inp, *pcb;
- struct sockaddr_in sin;
- struct proc *p = current_proc();
- int stat;
-
-
- pcbinfo->nat_dummy_socket.so_pcb = 0;
- pcbinfo->nat_dummy_socket.so_options = 0;
- if (*lport) {
- /* The grabber wants a particular port */
-
- if (faddr.s_addr || fport) {
- /*
- * This is either the second half of an active connect, or
- * it's from the acceptance of an incoming connection.
- */
- if (laddr.s_addr == 0) {
- pcbinfo->nat_dummy_socket.so_pcb = (caddr_t)pcbinfo->nat_dummy_pcb;
- return EINVAL;
- }
-
- inp = in_pcblookup_hash(pcbinfo, faddr, fport, laddr, *lport, 0, NULL);
- if (inp) {
- /* pcb was found, its count was upped. need to decrease it here */
- in_pcb_checkstate(inp, WNT_RELEASE, 0);
- if (!(IN_MULTICAST(ntohl(laddr.s_addr)))) {
- pcbinfo->nat_dummy_socket.so_pcb = (caddr_t)pcbinfo->nat_dummy_pcb;
- return (EADDRINUSE);
- }
- }
-
- stat = in_pcballoc(&pcbinfo->nat_dummy_socket, pcbinfo, p);
- if (stat) {
- pcbinfo->nat_dummy_socket.so_pcb = (caddr_t)pcbinfo->nat_dummy_pcb;
- return stat;
- }
- pcb = sotoinpcb(&pcbinfo->nat_dummy_socket);
- pcb->inp_vflag |= INP_IPV4;
-
- pcb->inp_lport = *lport;
- pcb->inp_laddr.s_addr = laddr.s_addr;
-
- pcb->inp_faddr = faddr;
- pcb->inp_fport = fport;
-
- lck_rw_lock_exclusive(pcbinfo->mtx);
- in_pcbinshash(pcb, 1);
- lck_rw_done(pcbinfo->mtx);
- }
- else {
- /*
- * This is either a bind for a passive socket, or it's the
- * first part of bind-connect sequence (not likely since an
- * ephemeral port is usually used in this case). Or, it's
- * the result of a connection acceptance when the foreign
- * address/port cannot be provided (which requires the SO_REUSEADDR
- * flag if laddr is not multicast).
- */
-
- stat = in_pcballoc(&pcbinfo->nat_dummy_socket, pcbinfo, p);
- if (stat) {
- pcbinfo->nat_dummy_socket.so_pcb = (caddr_t)pcbinfo->nat_dummy_pcb;
- return stat;
- }
- pcb = sotoinpcb(&pcbinfo->nat_dummy_socket);
- pcb->inp_vflag |= INP_IPV4;
-
- pcbinfo->nat_dummy_socket.so_options = options;
- bzero(&sin, sizeof(struct sockaddr_in));
- sin.sin_len = sizeof(struct sockaddr_in);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = laddr.s_addr;
- sin.sin_port = *lport;
-
- socket_lock(&pcbinfo->nat_dummy_socket, 1);
- stat = in_pcbbind((struct inpcb *) pcbinfo->nat_dummy_socket.so_pcb,
- (struct sockaddr *) &sin, p);
- if (stat) {
- socket_unlock(&pcbinfo->nat_dummy_socket, 1); /*detach first */
- in_pcb_detach_port(pcb); /* will restore dummy pcb */
- return stat;
- }
- socket_unlock(&pcbinfo->nat_dummy_socket, 1);
- }
- }
- else {
- /* The grabber wants an ephemeral port */
-
- stat = in_pcballoc(&pcbinfo->nat_dummy_socket, pcbinfo, p);
- if (stat) {
- pcbinfo->nat_dummy_socket.so_pcb = (caddr_t)pcbinfo->nat_dummy_pcb;
- return stat;
- }
- pcb = sotoinpcb(&pcbinfo->nat_dummy_socket);
- pcb->inp_vflag |= INP_IPV4;
-
- bzero(&sin, sizeof(struct sockaddr_in));
- sin.sin_len = sizeof(struct sockaddr_in);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = laddr.s_addr;
- sin.sin_port = 0;
-
- if (faddr.s_addr || fport) {
- /*
- * Not sure if this case will be used - could occur when connect
- * is called, skipping the bind.
- */
-
- if (laddr.s_addr == 0) {
- in_pcb_detach_port(pcb); /* restores dummy pcb */
- return EINVAL;
- }
-
- socket_lock(&pcbinfo->nat_dummy_socket, 1);
- stat = in_pcbbind((struct inpcb *) pcbinfo->nat_dummy_socket.so_pcb,
- (struct sockaddr *) &sin, p);
- if (stat) {
- socket_unlock(&pcbinfo->nat_dummy_socket, 1);
- in_pcb_detach_port(pcb); /* restores dummy pcb */
- return stat;
- }
-
- socket_unlock(&pcbinfo->nat_dummy_socket, 1);
- inp = in_pcblookup_hash(pcbinfo, faddr, fport,
- pcb->inp_laddr, pcb->inp_lport, 0, NULL);
- if (inp) {
- /* pcb was found, its count was upped. need to decrease it here */
- in_pcb_checkstate(inp, WNT_RELEASE, 0);
- in_pcb_detach_port(pcb);
- return (EADDRINUSE);
- }
-
- lck_rw_lock_exclusive(pcbinfo->mtx);
- pcb->inp_faddr = faddr;
- pcb->inp_fport = fport;
- in_pcbrehash(pcb);
- lck_rw_done(pcbinfo->mtx);
- }
- else {
- /*
- * This is a simple bind of an ephemeral port. The local addr
- * may or may not be defined.
- */
-
- socket_lock(&pcbinfo->nat_dummy_socket, 1);
- stat = in_pcbbind((struct inpcb *) pcbinfo->nat_dummy_socket.so_pcb,
- (struct sockaddr *) &sin, p);
- if (stat) {
- socket_unlock(&pcbinfo->nat_dummy_socket, 1);
- in_pcb_detach_port(pcb);
- return stat;
- }
- socket_unlock(&pcbinfo->nat_dummy_socket, 1);
- }
- *lport = pcb->inp_lport;
- }
-
-
- pcb->nat_owner = owner_id;
- pcb->nat_cookie = cookie;
- pcb->inp_ppcb = (caddr_t) pcbinfo->dummy_cb;
- pcbinfo->nat_dummy_socket.so_pcb = (caddr_t)pcbinfo->nat_dummy_pcb; /* restores dummypcb */
- return 0;
-}
-
-/* 3962035 - in_pcb_letgo_port needs a special case function for detaching */
-static void
-in_pcb_detach_port(
- struct inpcb *inp)
-{
- struct socket *so = inp->inp_socket;
- struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
-
- if (so != &pcbinfo->nat_dummy_socket)
- panic("in_pcb_detach_port: not a dummy_sock: so=%p, inp=%p\n", so, inp);
- inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
- /*### access ipi in in_pcbremlists */
- in_pcbremlists(inp);
-
- inp->inp_socket = 0;
- zfree(pcbinfo->ipi_zone, inp);
- pcbinfo->nat_dummy_socket.so_pcb = (caddr_t)pcbinfo->nat_dummy_pcb; /* restores dummypcb */
-}
-
-int
-in_pcb_letgo_port(struct inpcbinfo *pcbinfo, struct in_addr laddr, u_short lport,
- struct in_addr faddr, u_short fport, u_char owner_id)
-{
- struct inpcbhead *head;
- struct inpcb *inp;
-
- /*
- * First look for an exact match.
- */
-
- lck_rw_lock_exclusive(pcbinfo->mtx);
- head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)];
- for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
- if (inp->inp_faddr.s_addr == faddr.s_addr &&
- inp->inp_laddr.s_addr == laddr.s_addr &&
- inp->inp_fport == fport &&
- inp->inp_lport == lport &&
- inp->nat_owner == owner_id) {
- /*
- * Found.
- */
- in_pcb_detach_port(inp);
- lck_rw_done(pcbinfo->mtx);
- return 0;
- }
- }
-
- lck_rw_done(pcbinfo->mtx);
- return ENOENT;
-}
-
-u_char
-in_pcb_get_owner(struct inpcbinfo *pcbinfo,
- struct in_addr laddr, u_short lport,
- struct in_addr faddr, u_short fport,
- u_int *cookie)
-
-{
- struct inpcb *inp;
- u_char owner_id = INPCB_NO_OWNER;
- struct inpcbport *phd;
- struct inpcbporthead *porthash;
-
-
- if (IN_MULTICAST(laddr.s_addr)) {
- /*
- * Walk through PCB's looking for registered
- * owners.
- */
-
- lck_rw_lock_shared(pcbinfo->mtx);
- porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport,
- pcbinfo->porthashmask)];
- for (phd = porthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) {
- if (phd->phd_port == lport)
- break;
- }
-
- if (phd == 0) {
- lck_rw_done(pcbinfo->mtx);
- return INPCB_NO_OWNER;
- }
-
- owner_id = INPCB_NO_OWNER;
- for (inp = phd->phd_pcblist.lh_first; inp != NULL;
- inp = inp->inp_portlist.le_next) {
-
- if (inp->inp_laddr.s_addr == laddr.s_addr) {
- if (inp->nat_owner == 0)
- owner_id |= INPCB_OWNED_BY_X;
- else
- owner_id |= inp->nat_owner;
- }
- }
-
- lck_rw_done(pcbinfo->mtx);
- return owner_id;
- }
- else {
- inp = in_pcblookup_hash(pcbinfo, faddr, fport,
- laddr, lport, 1, NULL);
- if (inp) {
- /* pcb was found, its count was upped. need to decrease it here */
- /* if we found it, that pcb is already locked by the caller */
- if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING)
- return(INPCB_NO_OWNER);
-
- if (inp->nat_owner) {
- owner_id = inp->nat_owner;
- *cookie = inp->nat_cookie;
- }
- else {
- owner_id = INPCB_OWNED_BY_X;
- }
- }
- else
- owner_id = INPCB_NO_OWNER;
-
- return owner_id;
- }
-}
-
-int
-in_pcb_new_share_client(struct inpcbinfo *pcbinfo, u_char *owner_id)
-{
-
- int i;
-
-
- for (i=0; i < INPCB_MAX_IDS; i++) {
- if ((pcbinfo->all_owners & (1 << i)) == 0) {
- pcbinfo->all_owners |= (1 << i);
- *owner_id = (1 << i);
- return 0;
- }
- }
-
- return ENOSPC;
-}
-
-int
-in_pcb_rem_share_client(struct inpcbinfo *pcbinfo, u_char owner_id)
-{
- struct inpcb *inp;
-
-
- lck_rw_lock_exclusive(pcbinfo->mtx);
- if (pcbinfo->all_owners & owner_id) {
- pcbinfo->all_owners &= ~owner_id;
- for (inp = pcbinfo->listhead->lh_first; inp != NULL; inp = inp->inp_list.le_next) {
- if (inp->nat_owner & owner_id) {
- if (inp->nat_owner == owner_id)
- /*
- * Deallocate the pcb
- */
- in_pcb_detach_port(inp);
- else
- inp->nat_owner &= ~owner_id;
- }
- }
- }
- else {
- lck_rw_done(pcbinfo->mtx);
- return ENOENT;
- }
-
- lck_rw_done(pcbinfo->mtx);
- return 0;
-}
-
-
-
-void in_pcb_nat_init(struct inpcbinfo *pcbinfo, int afamily,
- int pfamily, int protocol)
-{
- int stat;
- struct proc *p = current_proc();
-
- bzero(&pcbinfo->nat_dummy_socket, sizeof(struct socket));
-#if CONFIG_MACF_NET
- mac_socket_label_init(&pcbinfo->nat_dummy_socket, M_WAITOK);
-#endif
- pcbinfo->nat_dummy_socket.so_proto = pffindproto_locked(afamily, pfamily, protocol);
- pcbinfo->all_owners = 0;
- stat = in_pcballoc(&pcbinfo->nat_dummy_socket, pcbinfo, p);
- if (stat)
- panic("in_pcb_nat_init: can't alloc fakepcb err=%d\n", stat);
- pcbinfo->nat_dummy_pcb = (struct inpcb *)pcbinfo->nat_dummy_socket.so_pcb;
-}
-