Example #1
0
static int conn_read (void)
{
  struct inpcbtable table;
  struct inpcb *head;
  struct inpcb *next;
  struct inpcb inpcb;
  struct tcpcb tcpcb;
  int status;

  conn_reset_port_entry ();

  /* Read the pcbtable from the kernel */
  status = kread (inpcbtable_off, &table, sizeof (table));
  if (status != 0)
    return (-1);

  /* Get the `head' pcb */
  head = (struct inpcb *) &(inpcbtable_ptr->inpt_queue);
  /* Get the first pcb */
  next = (struct inpcb *)CIRCLEQ_FIRST (&table.inpt_queue);

  while (next != head)
  {
    /* Read the pcb pointed to by `next' into `inpcb' */
    kread ((u_long) next, &inpcb, sizeof (inpcb));

    /* Advance `next' */
    next = (struct inpcb *)CIRCLEQ_NEXT (&inpcb, inp_queue);

    /* Ignore sockets, that are not connected. */
#ifdef __NetBSD__
    if (inpcb.inp_af == AF_INET6)
      continue; /* XXX see netbsd/src/usr.bin/netstat/inet6.c */
#else
    if (!(inpcb.inp_flags & INP_IPV6)
	&& (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY))
      continue;
    if ((inpcb.inp_flags & INP_IPV6)
	&& IN6_IS_ADDR_UNSPECIFIED (&inpcb.inp_laddr6))
      continue;
#endif

    kread ((u_long) inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
    conn_handle_ports (ntohs(inpcb.inp_lport), ntohs(inpcb.inp_fport), tcpcb.t_state);
  } /* while (next != head) */

  conn_submit_all ();

  return (0);
}
Example #2
0
void
in6_pcbpurgeif(struct inpcbtable *table, struct ifnet *ifp)
{
	struct rtentry *rt;
	struct in6pcb *in6p, *nin6p;

	for (in6p = (struct in6pcb *)CIRCLEQ_FIRST(&table->inpt_queue);
	    in6p != (void *)&table->inpt_queue;
	    in6p = nin6p) {
		nin6p = (struct in6pcb *)CIRCLEQ_NEXT(in6p, in6p_queue);
		if (in6p->in6p_af != AF_INET6)
			continue;
		if ((rt = rtcache_validate(&in6p->in6p_route)) != NULL &&
		    rt->rt_ifp == ifp)
			in6_rtchange(in6p, 0);
	}
}
Example #3
0
void
in6_pcbpurgeif0(struct inpcbtable *table, struct ifnet *ifp)
{
	struct in6pcb *in6p, *nin6p;
	struct ip6_moptions *im6o;
	struct in6_multi_mship *imm, *nimm;

	for (in6p = (struct in6pcb *)CIRCLEQ_FIRST(&table->inpt_queue);
	    in6p != (void *)&table->inpt_queue;
	    in6p = nin6p) {
		nin6p = (struct in6pcb *)CIRCLEQ_NEXT(in6p, in6p_queue);
		if (in6p->in6p_af != AF_INET6)
			continue;

		im6o = in6p->in6p_moptions;
		if (im6o) {
			/*
			 * Unselect the outgoing interface if it is being
			 * detached.
			 */
			if (im6o->im6o_multicast_ifp == ifp)
				im6o->im6o_multicast_ifp = NULL;

			/*
			 * Drop multicast group membership if we joined
			 * through the interface being detached.
			 * XXX controversial - is it really legal for kernel
			 * to force this?
			 */
			for (imm = im6o->im6o_memberships.lh_first;
			     imm != NULL; imm = nimm) {
				nimm = imm->i6mm_chain.le_next;
				if (imm->i6mm_maddr->in6m_ifp == ifp) {
					LIST_REMOVE(imm, i6mm_chain);
					in6_leavegroup(imm);
				}
			}
		}
	}
}
int
port_in_use(const char *if_name,
            unsigned eport, int proto,
            const char *iaddr, unsigned iport)
{
	int found = 0;
	char ip_addr_str[INET_ADDRSTRLEN];
	struct in_addr ip_addr;
#ifdef __linux__
	/* linux code */
	char line[256];
	FILE *f;
	const char * tcpfile = "/proc/net/tcp";
	const char * udpfile = "/proc/net/udp";
#endif

	if(getifaddr(if_name, ip_addr_str, INET_ADDRSTRLEN, &ip_addr, NULL) < 0) {
		ip_addr.s_addr = 0;
		ip_addr_str[0] = '\0';
	}

	syslog(LOG_DEBUG, "Check protocol %s for port %d on ext_if %s %s, %08X",
	    (proto==IPPROTO_TCP)?"tcp":"udp", eport, if_name,
	    ip_addr_str, (unsigned)ip_addr.s_addr);

	/* Phase 1 : check for local sockets (would be listed by netstat) */
#if defined(__linux__)
	f = fopen((proto==IPPROTO_TCP)?tcpfile:udpfile, "r");
	if (!f) {
		syslog(LOG_ERR, "cannot open %s", (proto==IPPROTO_TCP)?tcpfile:udpfile);
		return -1;
	}

	while (fgets(line, 255, f)) {
		char eaddr[68];
		unsigned tmp_port;
		if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
				"%*x:%*x %*x %*d %*d %*llu",
				eaddr, &tmp_port) == 2
		) {
			/* TODO add IPV6 support if enabled
			 * Presently assumes IPV4 */
#ifdef DEBUG
			syslog(LOG_DEBUG, "port_in_use check port %d and address %s", tmp_port, eaddr);
#endif
			if (tmp_port == eport) {
				char tmp_addr[4];
				struct in_addr *tmp_ip_addr = (struct in_addr *)tmp_addr;
				if (sscanf(eaddr,"%2hhx%2hhx%2hhx%2hhx",
					&tmp_addr[3],&tmp_addr[2],&tmp_addr[1],&tmp_addr[0]) == 4)
				{
					if (tmp_ip_addr->s_addr == 0 || tmp_ip_addr->s_addr == ip_addr.s_addr)
					{
						found++;
						break;  /* don't care how many, just that we found at least one */
					}
				}
			}
		}
	}
	fclose(f);

#elif defined(__OpenBSD__)
static struct nlist list[] = {
#if 0
        {"_tcpstat", 0, 0, 0, 0},
        {"_udpstat", 0, 0, 0, 0},
        {"_tcbinfo", 0, 0, 0, 0},
        {"_udbinfo", 0, 0, 0, 0},
#endif
		{"_tcbtable", 0, 0, 0, 0},
		{"_udbtable", 0, 0, 0, 0},
        {NULL,0, 0, 0, 0}
};
	char errstr[_POSIX2_LINE_MAX];
	kvm_t *kd;
	ssize_t n;
	struct inpcbtable table;
	struct inpcb *next;
	struct inpcb inpcb;
	kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errstr);
	if(!kd) {
		syslog(LOG_ERR, "%s: kvm_openfiles(): %s",
		       "portinuse()", errstr);
		return -1;
	}
	if(kvm_nlist(kd, list) < 0) {
		syslog(LOG_ERR, "%s: kvm_nlist(): %s",
		       "portinuse()", kvm_geterr(kd));
		kvm_close(kd);
		return -1;
	}
	n = kvm_read(kd, list[(proto==IPPROTO_TCP)?0:1].n_value, &table, sizeof(table));
	if(n < 0) {
		syslog(LOG_ERR, "%s: kvm_read(): %s",
		       "portinuse()", kvm_geterr(kd));
		kvm_close(kd);
		return -1;
	}
	next = CIRCLEQ_FIRST(&table.inpt_queue); /*TAILQ_FIRST(&table.inpt_queue);*/
	while(next != NULL) {
		if(((u_long)next & 3) != 0) break;
		n = kvm_read(kd, (u_long)next, &inpcb, sizeof(inpcb));
		if(n < 0) {
			syslog(LOG_ERR, "kvm_read(): %s", kvm_geterr(kd));
			break;
		}
		next = CIRCLEQ_NEXT(&inpcb, inp_queue);	/*TAILQ_NEXT(&inpcb, inp_queue);*/
		/* skip IPv6 sockets */
		if((inpcb.inp_flags & INP_IPV6) != 0)
			continue;
#ifdef DEBUG
		syslog(LOG_DEBUG, "%08lx:%hu %08lx:%hu",
		       (u_long)inpcb.inp_laddr.s_addr, ntohs(inpcb.inp_lport),
		       (u_long)inpcb.inp_faddr.s_addr, ntohs(inpcb.inp_fport));
#endif
		if(eport == (unsigned)ntohs(inpcb.inp_lport)) {
			if(inpcb.inp_laddr.s_addr == INADDR_ANY || inpcb.inp_laddr.s_addr == ip_addr.s_addr) {
				found++;
				break;  /* don't care how many, just that we found at least one */
			}
		}
	}
	kvm_close(kd);

#elif defined(__DragonFly__)
	const char *varname;
	struct xinpcb *xip;
	struct xtcpcb *xtp;
	struct inpcb *inp;
	void *buf = NULL;
	void *so_begin, *so_end;

	size_t len;

	switch (proto) {
	case IPPROTO_TCP:
		varname = "net.inet.tcp.pcblist";
		break;
	case IPPROTO_UDP:
		varname = "net.inet.udp.pcblist";
		break;
	default:
		syslog(LOG_ERR, "port_in_use() unknown proto=%d", proto);
		return -1;
	}

	if (sysctlbyname(varname, NULL, &len, NULL, 0) < 0) {
		syslog(LOG_ERR, "sysctlbyname(%s, NULL, ...): %m", varname);
		return -1;
	}
	buf = malloc(len);
	if (buf == NULL) {
		syslog(LOG_ERR, "malloc(%u) failed", (unsigned)len);
		return -1;
	}
	if (sysctlbyname(varname, buf, &len, NULL, 0) < 0) {
		syslog(LOG_ERR, "sysctlbyname(%s, buf, ...): %m", varname);
		free(buf);
		return -1;
	}

	so_begin = buf;
	so_end = (uint8_t *)buf + len;
	for (so_begin = buf, so_end = (uint8_t *)so_begin + len;
	     (uint8_t *)so_begin + sizeof(size_t) < (uint8_t *)so_end &&
	     (uint8_t *)so_begin + *(size_t *)so_begin <= (uint8_t *)so_end;
	     so_begin = (uint8_t *)so_begin + *(size_t *)so_begin) {
		switch (proto) {
		case IPPROTO_TCP:
			xtp = (struct xtcpcb *)so_begin;
			if (xtp->xt_len != sizeof *xtp) {
				syslog(LOG_WARNING, "struct xtcpcb size mismatch; %ld vs %ld",
				       (long)xtp->xt_len, sizeof *xtp);
				free(buf);
				return -1;
			}
			inp = &xtp->xt_inp;
			break;
		case IPPROTO_UDP:
			xip = (struct xinpcb *)so_begin;
			if (xip->xi_len != sizeof *xip) {
				syslog(LOG_WARNING, "struct xinpcb size mismatch : %ld vs %ld",
				       (long)xip->xi_len, sizeof *xip);
				free(buf);
				return -1;
			}
			inp = &xip->xi_inp;
			break;
		default:
			abort();
		}
		/* no support for IPv6 */
		if ((inp->inp_vflag & INP_IPV6) != 0)
			continue;
		syslog(LOG_DEBUG, "%08lx:%hu %08lx:%hu <=> %hu %08lx:%hu",
		       (u_long)inp->inp_laddr.s_addr, ntohs(inp->inp_lport),
		       (u_long)inp->inp_faddr.s_addr, ntohs(inp->inp_fport),
		       eport, (u_long)ip_addr.s_addr, iport
		);
		if (eport == (unsigned)ntohs(inp->inp_lport)) {
			if (inp->inp_laddr.s_addr == INADDR_ANY || inp->inp_laddr.s_addr == ip_addr.s_addr) {
				found++;
				break;  /* don't care how many, just that we found at least one */
			}
		}
	}
	if(buf) {
		free(buf);
		buf = NULL;
	}
#elif defined(__FreeBSD__)
	const char *varname;
	struct xinpgen *xig, *exig;
	struct xinpcb *xip;
	struct xtcpcb *xtp;
	struct inpcb *inp;
	void *buf = NULL;
	size_t len;

	switch (proto) {
	case IPPROTO_TCP:
		varname = "net.inet.tcp.pcblist";
		break;
	case IPPROTO_UDP:
		varname = "net.inet.udp.pcblist";
		break;
	default:
		syslog(LOG_ERR, "port_in_use() unknown proto=%d", proto);
		return -1;
	}

	if (sysctlbyname(varname, NULL, &len, NULL, 0) < 0) {
		syslog(LOG_ERR, "sysctlbyname(%s, NULL, ...): %m", varname);
		return -1;
	}
	buf = malloc(len);
	if (buf == NULL) {
		syslog(LOG_ERR, "malloc(%u) failed", (unsigned)len);
		return -1;
	}
	if (sysctlbyname(varname, buf, &len, NULL, 0) < 0) {
		syslog(LOG_ERR, "sysctlbyname(%s, buf, ...): %m", varname);
		free(buf);
		return -1;
	}

	xig = (struct xinpgen *)buf;
	exig = (struct xinpgen *)(void *)((char *)buf + len - sizeof *exig);
	if (xig->xig_len != sizeof *xig) {
		syslog(LOG_WARNING, "struct xinpgen size mismatch; %ld vs %ld",
		       (long)xig->xig_len, sizeof *xig);
		free(buf);
		return -1;
	}
	if (exig->xig_len != sizeof *exig) {
		syslog(LOG_WARNING, "struct xinpgen size mismatch; %ld vs %ld",
		       (long)exig->xig_len, sizeof *exig);
		free(buf);
		return -1;
	}

	while (1) {
		xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
		if (xig >= exig)
			break;
		switch (proto) {
		case IPPROTO_TCP:
			xtp = (struct xtcpcb *)xig;
			if (xtp->xt_len != sizeof *xtp) {
				syslog(LOG_WARNING, "struct xtcpcb size mismatch; %ld vs %ld",
				       (long)xtp->xt_len, sizeof *xtp);
				free(buf);
				return -1;
			}
			inp = &xtp->xt_inp;
			break;
		case IPPROTO_UDP:
			xip = (struct xinpcb *)xig;
			if (xip->xi_len != sizeof *xip) {
				syslog(LOG_WARNING, "struct xinpcb size mismatch : %ld vs %ld",
				       (long)xip->xi_len, sizeof *xip);
				free(buf);
				return -1;
			}
			inp = &xip->xi_inp;
			break;
		default:
			abort();
		}
		/* no support for IPv6 */
		if ((inp->inp_vflag & INP_IPV6) != 0)
			continue;
		syslog(LOG_DEBUG, "%08lx:%hu %08lx:%hu <=> %hu %08lx:%hu",
		       (u_long)inp->inp_laddr.s_addr, ntohs(inp->inp_lport),
		       (u_long)inp->inp_faddr.s_addr, ntohs(inp->inp_fport),
		       eport, (u_long)ip_addr.s_addr, iport
		);
		if (eport == (unsigned)ntohs(inp->inp_lport)) {
			if (inp->inp_laddr.s_addr == INADDR_ANY || inp->inp_laddr.s_addr == ip_addr.s_addr) {
				found++;
				break;  /* don't care how many, just that we found at least one */
			}
		}
	}
	if (buf) {
		free(buf);
		buf = NULL;
	}
/* #elif __NetBSD__ */
#else
/* TODO : NetBSD / Darwin (OS X) / Solaris code */
#error "No port_in_use() implementation available for this OS"
#endif

	/* Phase 2 : check existing mappings
	 * TODO : implement for pf/ipfw/etc. */
#if defined(USE_NETFILTER)
	if (!found) {
		char iaddr_old[16];
		unsigned short iport_old;
		int i;
		for (i = 0; chains_to_check[i]; i++) {
			if (get_nat_redirect_rule(chains_to_check[i], if_name, eport, proto,
					iaddr_old, sizeof(iaddr_old), &iport_old,
					0, 0, 0, 0, 0, 0, 0) == 0)
			{
				syslog(LOG_DEBUG, "port_in_use check port %d on nat chain %s redirected to %s port %d", eport,
						chains_to_check[i], iaddr_old, iport_old);
				if (!(strcmp(iaddr, iaddr_old)==0 && iport==iport_old)) {
					/* only "in use" if redirected to somewhere else */
					found++;
					break;  /* don't care how many, just that we found at least one */
				}
			}
		}
	}
#else /* USE_NETFILTER */
	UNUSED(iport); UNUSED(iaddr);
#endif /* USE_NETFILTER */
	return found;
}
Example #5
0
int
read_ns(void)
{
	struct inpcbtable pcbtable;
	struct inpcb *head, *prev, *next;
	struct inpcb inpcb;
	struct socket sockb;
	struct tcpcb tcpcb;
	void *off;
	int istcp;

	if (kd == NULL) {
		return (0);
	}

	num_ns = 0;

	if (namelist[X_TCBTABLE].n_value == 0)
		return 0;

	if (protos & TCP) {
		off = NPTR(X_TCBTABLE);
		istcp = 1;
	} else if (protos & UDP) {
		off = NPTR(X_UDBTABLE);
		istcp = 0;
	} else {
		error("No protocols to display");
		return 0;
	}

again:
	KREAD(off, &pcbtable, sizeof (struct inpcbtable));

	prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
	next = CIRCLEQ_FIRST(&pcbtable.inpt_queue);

	while (next != head) {
		KREAD(next, &inpcb, sizeof (inpcb));
		if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) {
			error("Kernel state in transition");
			return 0;
		}
		prev = next;
		next = CIRCLEQ_NEXT(&inpcb, inp_queue);

		if (!aflag) {
			if (!(inpcb.inp_flags & INP_IPV6) &&
			    inet_lnaof(inpcb.inp_faddr) == INADDR_ANY)
				continue;
			if ((inpcb.inp_flags & INP_IPV6) &&
			    IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_faddr6))
				continue;
		}
		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
		if (istcp) {
			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
			if (!aflag && tcpcb.t_state <= TCPS_LISTEN)
				continue;
			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
		} else
			enter(&inpcb, &sockb, 0, "udp");
	}
	if (istcp && (protos & UDP)) {
		istcp = 0;
		off = NPTR(X_UDBTABLE);
		goto again;
	}

	num_disp = num_ns;
	return 0;
}
Example #6
0
/**
 *
 * @retval  0 no errors
 * @retval !0 errors
 */
static int
_load(netsnmp_container *container, u_int load_flags)
{
    struct inpcbtable table;
    struct inpcb   *head, *next, *prev;
    struct inpcb   inpcb;
    struct tcpcb   tcpcb;
    int      StateMap[] = { 1, 2, 3, 4, 5, 8, 6, 10, 9, 7, 11 };
    netsnmp_tcpconn_entry  *entry;
    int      state;
    int      rc = 0;

    /*
     *  Read in the buffer containing the TCP table data
     */
    if (!auto_nlist(TCP_SYMBOL, (char *)&table, sizeof(table))) {
	DEBUGMSGTL(("tcp-mib/tcpConn_openbsd", "Failed to read tcp_symbol\n"));
	return -1;
    }

    prev = (struct inpcb *)&CIRCLEQ_FIRST(&table.inpt_queue);
    prev = NULL;
    head = next = CIRCLEQ_FIRST(&table.inpt_queue);

    while (next) {
	if (!NETSNMP_KLOOKUP(next, (char *)&inpcb, sizeof(inpcb))) {
	    DEBUGMSGTL(("tcp-mib/data_access/tcpConn", "klookup inpcb failed\n"));
	    break;
	}
	if (prev && CIRCLEQ_PREV(&inpcb, inp_queue) != prev) {
	    snmp_log(LOG_ERR,"tcbtable link error\n");
	    break;
	}
	prev = next;
	next = CIRCLEQ_NEXT(&inpcb, inp_queue);
	if (!NETSNMP_KLOOKUP(inpcb.inp_ppcb, (char *)&tcpcb, sizeof(tcpcb))) {
	    DEBUGMSGTL(("tcp-mib/data_access/tcpConn", "klookup tcpcb failed\n"));
	    break;
	}
	state = StateMap[tcpcb.t_state];

	if (load_flags) {
	    if (state == TCPCONNECTIONSTATE_LISTEN) {
		if (load_flags & NETSNMP_ACCESS_TCPCONN_LOAD_NOLISTEN) {
		    DEBUGMSGT(("verbose:access:tcpconn:container",
			       " skipping listen\n"));
		    goto skip;
		}
	    }
	    else if (load_flags & NETSNMP_ACCESS_TCPCONN_LOAD_ONLYLISTEN) {
		DEBUGMSGT(("verbose:access:tcpconn:container",
			    " skipping non-listen\n"));
		goto skip;
	    }
	}

#if !defined(NETSNMP_ENABLE_IPV6)
        if (inpcb.inp_flags & INP_IPV6)
	    goto skip;
#endif

        entry = netsnmp_access_tcpconn_entry_create();
        if(NULL == entry) {
            rc = -3;
            break;
        }

        /** oddly enough, these appear to already be in network order */
        entry->loc_port = ntohs(inpcb.inp_lport);
        entry->rmt_port = ntohs(inpcb.inp_fport);
        entry->tcpConnState = state;
        entry->pid = 0;
        
        /** the addr string may need work */
	if (inpcb.inp_flags & INP_IPV6) {
	    entry->loc_addr_len = entry->rmt_addr_len = 16;
	    memcpy(entry->loc_addr, &inpcb.inp_laddr6, 16);
	    memcpy(entry->rmt_addr, &inpcb.inp_faddr6, 16);
	}
	else {
	    entry->loc_addr_len = entry->rmt_addr_len = 4;
	    memcpy(entry->loc_addr, &inpcb.inp_laddr, 4);
	    memcpy(entry->rmt_addr, &inpcb.inp_faddr, 4);
	}
	DEBUGMSGTL(("tcp-mib/data_access", "tcp %d %d %d\n",
	    entry->loc_addr_len, entry->loc_port, entry->rmt_port));

        /*
         * add entry to container
         */
        entry->arbitrary_index = CONTAINER_SIZE(container) + 1;
        CONTAINER_INSERT(container, entry);
skip:
	if (head == next)
	    break;
    }

    if(rc<0)
        return rc;

    return 0;
}
Example #7
0
/*
 * Pass some notification to all connections of a protocol
 * associated with address dst.  The local address and/or port numbers
 * may be specified to limit the search.  The "usual action" will be
 * taken, depending on the ctlinput cmd.  The caller must filter any
 * cmds that are uninteresting (e.g., no error in the map).
 * Call the protocol specific routine (if any) to report
 * any errors for each matching socket.
 *
 * Also perform input-side security policy check
 *    once PCB to be notified has been located.
 *
 * Must be called at splnet.
 */
int
in6_pcbnotify(struct inpcbtable *head, struct sockaddr *dst, 
	uint fport_arg, struct sockaddr *src, uint lport_arg, int cmd, 
	void *cmdarg, void (*notify)(struct inpcb *, int))
{
	struct inpcb *inp, *ninp;
	u_short fport = fport_arg, lport = lport_arg;
	struct sockaddr_in6 sa6_src, *sa6_dst;
	int errno, nmatch = 0;
	u_int32_t flowinfo;

	if ((unsigned)cmd >= PRC_NCMDS || dst->sa_family != AF_INET6)
		return (0);

	sa6_dst = (struct sockaddr_in6 *)dst;
	if (IN6_IS_ADDR_UNSPECIFIED(&sa6_dst->sin6_addr))
		return (0);
	if (IN6_IS_ADDR_V4MAPPED(&sa6_dst->sin6_addr)) {
#ifdef DIAGNOSTIC
		printf("Huh?  Thought in6_pcbnotify() never got "
		       "called with mapped!\n");
#endif
		return (0);
	}

	/*
	 * note that src can be NULL when we get notify by local fragmentation.
	 */
	sa6_src = (src == NULL) ? sa6_any : *(struct sockaddr_in6 *)src;
	flowinfo = sa6_src.sin6_flowinfo;

	/*
	 * Redirects go to all references to the destination,
	 * and use in_rtchange to invalidate the route cache.
	 * Dead host indications: also use in_rtchange to invalidate
	 * the cache, and deliver the error to all the sockets.
	 * Otherwise, if we have knowledge of the local port and address,
	 * deliver only to that socket.
	 */
	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
		fport = 0;
		lport = 0;
		sa6_src.sin6_addr = in6addr_any;

		if (cmd != PRC_HOSTDEAD)
			notify = in_rtchange;
	}
	errno = inet6ctlerrmap[cmd];

	for (inp = CIRCLEQ_FIRST(&head->inpt_queue);
	     inp != CIRCLEQ_END(&head->inpt_queue); inp = ninp) {
		ninp = CIRCLEQ_NEXT(inp, inp_queue);

		if ((inp->inp_flags & INP_IPV6) == 0)
			continue;

		/*
		 * Under the following condition, notify of redirects
		 * to the pcb, without making address matches against inpcb.
		 * - redirect notification is arrived.
		 * - the inpcb is unconnected.
		 * - the inpcb is caching !RTF_HOST routing entry.
		 * - the ICMPv6 notification is from the gateway cached in the
		 *   inpcb.  i.e. ICMPv6 notification is from nexthop gateway
		 *   the inpcb used very recently.
		 *
		 * This is to improve interaction between netbsd/openbsd
		 * redirect handling code, and inpcb route cache code.
		 * without the clause, !RTF_HOST routing entry (which carries
		 * gateway used by inpcb right before the ICMPv6 redirect)
		 * will be cached forever in unconnected inpcb.
		 *
		 * There still is a question regarding to what is TRT:
		 * - On bsdi/freebsd, RTF_HOST (cloned) routing entry will be
		 *   generated on packet output.  inpcb will always cache
		 *   RTF_HOST routing entry so there's no need for the clause
		 *   (ICMPv6 redirect will update RTF_HOST routing entry,
		 *   and inpcb is caching it already).
		 *   However, bsdi/freebsd are vulnerable to local DoS attacks
		 *   due to the cloned routing entries.
		 * - Specwise, "destination cache" is mentioned in RFC2461.
		 *   Jinmei says that it implies bsdi/freebsd behavior, itojun
		 *   is not really convinced.
		 * - Having hiwat/lowat on # of cloned host route (redirect/
		 *   pmtud) may be a good idea.  netbsd/openbsd has it.  see
		 *   icmp6_mtudisc_update().
		 */
		if ((PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) &&
		    IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) &&
		    inp->inp_route.ro_rt &&
		    !(inp->inp_route.ro_rt->rt_flags & RTF_HOST)) {
			struct sockaddr_in6 *dst6;

			dst6 = (struct sockaddr_in6 *)&inp->inp_route.ro_dst;
			if (IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr,
			    &sa6_dst->sin6_addr))
				goto do_notify;
		}

		/*
		 * Detect if we should notify the error. If no source and
		 * destination ports are specified, but non-zero flowinfo and
		 * local address match, notify the error. This is the case
		 * when the error is delivered with an encrypted buffer
		 * by ESP. Otherwise, just compare addresses and ports
		 * as usual.
		 */
		if (lport == 0 && fport == 0 && flowinfo &&
		    inp->inp_socket != NULL &&
		    flowinfo == (inp->inp_flowinfo & IPV6_FLOWLABEL_MASK) &&
		    IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &sa6_src.sin6_addr))
			goto do_notify;
		else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6,
					     &sa6_dst->sin6_addr) ||
			 inp->inp_socket == 0 ||
			 (lport && inp->inp_lport != lport) ||
			 (!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) &&
			  !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6,
					      &sa6_src.sin6_addr)) ||
			 (fport && inp->inp_fport != fport)) {
			continue;
		}
	  do_notify:
		nmatch++;
		if (notify)
			(*notify)(inp, errno);
	}
	return (nmatch);
}
Example #8
0
/*
 * Pass some notification to all connections of a protocol
 * associated with address dst.  The local address and/or port numbers
 * may be specified to limit the search.  The "usual action" will be
 * taken, depending on the ctlinput cmd.  The caller must filter any
 * cmds that are uninteresting (e.g., no error in the map).
 * Call the protocol specific routine (if any) to report
 * any errors for each matching socket.
 *
 * Must be called at splsoftnet.
 *
 * Note: src (4th arg) carries the flowlabel value on the original IPv6
 * header, in sin6_flowinfo member.
 */
int
in6_pcbnotify(struct inpcbtable *table, const struct sockaddr *dst,
    u_int fport_arg, const struct sockaddr *src, u_int lport_arg, int cmd,
    void *cmdarg, void (*notify)(struct in6pcb *, int))
{
	struct rtentry *rt;
	struct in6pcb *in6p, *nin6p;
	struct sockaddr_in6 sa6_src;
	const struct sockaddr_in6 *sa6_dst;
	u_int16_t fport = fport_arg, lport = lport_arg;
	int errno;
	int nmatch = 0;
	u_int32_t flowinfo;

	if ((unsigned)cmd >= PRC_NCMDS || dst->sa_family != AF_INET6)
		return 0;

	sa6_dst = (const struct sockaddr_in6 *)dst;
	if (IN6_IS_ADDR_UNSPECIFIED(&sa6_dst->sin6_addr))
		return 0;

	/*
	 * note that src can be NULL when we get notify by local fragmentation.
	 */
	sa6_src = (src == NULL) ? sa6_any : *(const struct sockaddr_in6 *)src;
	flowinfo = sa6_src.sin6_flowinfo;

	/*
	 * Redirects go to all references to the destination,
	 * and use in6_rtchange to invalidate the route cache.
	 * Dead host indications: also use in6_rtchange to invalidate
	 * the cache, and deliver the error to all the sockets.
	 * Otherwise, if we have knowledge of the local port and address,
	 * deliver only to that socket.
	 */
	if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
		fport = 0;
		lport = 0;
		bzero((void *)&sa6_src.sin6_addr, sizeof(sa6_src.sin6_addr));

		if (cmd != PRC_HOSTDEAD)
			notify = in6_rtchange;
	}

	errno = inet6ctlerrmap[cmd];
	for (in6p = (struct in6pcb *)CIRCLEQ_FIRST(&table->inpt_queue);
	    in6p != (void *)&table->inpt_queue;
	    in6p = nin6p) {
		nin6p = (struct in6pcb *)CIRCLEQ_NEXT(in6p, in6p_queue);

		if (in6p->in6p_af != AF_INET6)
			continue;

		/*
		 * Under the following condition, notify of redirects
		 * to the pcb, without making address matches against inpcb.
		 * - redirect notification is arrived.
		 * - the inpcb is unconnected.
		 * - the inpcb is caching !RTF_HOST routing entry.
		 * - the ICMPv6 notification is from the gateway cached in the
		 *   inpcb.  i.e. ICMPv6 notification is from nexthop gateway
		 *   the inpcb used very recently.
		 *
		 * This is to improve interaction between netbsd/openbsd
		 * redirect handling code, and inpcb route cache code.
		 * without the clause, !RTF_HOST routing entry (which carries
		 * gateway used by inpcb right before the ICMPv6 redirect)
		 * will be cached forever in unconnected inpcb.
		 *
		 * There still is a question regarding to what is TRT:
		 * - On bsdi/freebsd, RTF_HOST (cloned) routing entry will be
		 *   generated on packet output.  inpcb will always cache
		 *   RTF_HOST routing entry so there's no need for the clause
		 *   (ICMPv6 redirect will update RTF_HOST routing entry,
		 *   and inpcb is caching it already).
		 *   However, bsdi/freebsd are vulnerable to local DoS attacks
		 *   due to the cloned routing entries.
		 * - Specwise, "destination cache" is mentioned in RFC2461.
		 *   Jinmei says that it implies bsdi/freebsd behavior, itojun
		 *   is not really convinced.
		 * - Having hiwat/lowat on # of cloned host route (redirect/
		 *   pmtud) may be a good idea.  netbsd/openbsd has it.  see
		 *   icmp6_mtudisc_update().
		 */
		if ((PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) &&
		    IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
		    (rt = rtcache_validate(&in6p->in6p_route)) != NULL &&
		    !(rt->rt_flags & RTF_HOST)) {
			const struct sockaddr_in6 *dst6;

			dst6 = (const struct sockaddr_in6 *)
			    rtcache_getdst(&in6p->in6p_route);
			if (dst6 == NULL)
				;
			else if (IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr,
			    &sa6_dst->sin6_addr))
				goto do_notify;
		}

		/*
		 * If the error designates a new path MTU for a destination
		 * and the application (associated with this socket) wanted to
		 * know the value, notify. Note that we notify for all
		 * disconnected sockets if the corresponding application
		 * wanted. This is because some UDP applications keep sending
		 * sockets disconnected.
		 * XXX: should we avoid to notify the value to TCP sockets?
		 */
		if (cmd == PRC_MSGSIZE && (in6p->in6p_flags & IN6P_MTU) != 0 &&
		    (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) ||
		     IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &sa6_dst->sin6_addr))) {
			ip6_notify_pmtu(in6p, (const struct sockaddr_in6 *)dst,
					(u_int32_t *)cmdarg);
		}

		/*
		 * Detect if we should notify the error. If no source and
		 * destination ports are specified, but non-zero flowinfo and
		 * local address match, notify the error. This is the case
		 * when the error is delivered with an encrypted buffer
		 * by ESP. Otherwise, just compare addresses and ports
		 * as usual.
		 */
		if (lport == 0 && fport == 0 && flowinfo &&
		    in6p->in6p_socket != NULL &&
		    flowinfo == (in6p->in6p_flowinfo & IPV6_FLOWLABEL_MASK) &&
		    IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &sa6_src.sin6_addr))
			goto do_notify;
		else if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr,
					     &sa6_dst->sin6_addr) ||
		    in6p->in6p_socket == 0 ||
		    (lport && in6p->in6p_lport != lport) ||
		    (!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) &&
		     !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr,
					 &sa6_src.sin6_addr)) ||
		    (fport && in6p->in6p_fport != fport))
			continue;

	  do_notify:
		if (notify)
			(*notify)(in6p, errno);
		nmatch++;
	}
	return nmatch;
}
Example #9
0
/**
 *
 * @retval  0 no errors
 * @retval !0 errors
 */
static int
_load(netsnmp_container *container, u_int load_flags)
{
    struct inpcbtable table;
    struct inpcb   *head, *next, *prev;
    struct inpcb   inpcb;
    netsnmp_udp_endpoint_entry  *entry;
    int      rc = 0;

    /*
     *  Read in the buffer containing the TCP table data
     */
    if (!auto_nlist(UDB_SYMBOL, (char *) &table, sizeof(table))) {
	DEBUGMSGTL(("udp-mib/udp_endpoint_openbsd", "Failed to read udp_symbol\n"));
	return -1;
    }

    prev = (struct inpcb *)&CIRCLEQ_FIRST(&table.inpt_queue);
    prev = NULL;
    head = next = CIRCLEQ_FIRST(&table.inpt_queue);

    while (next) {
	NETSNMP_KLOOKUP(next, (char *)&inpcb, sizeof(inpcb));
	if (prev && CIRCLEQ_PREV(&inpcb, inp_queue) != prev) {
	    snmp_log(LOG_ERR,"udbtable link error\n");
	    break;
	}
	prev = next;
	next = CIRCLEQ_NEXT(&inpcb, inp_queue);

#if !defined(NETSNMP_ENABLE_IPV6)
        if (inpcb.inp_flags & INP_IPV6)
            goto skip;
#endif
        entry = netsnmp_access_udp_endpoint_entry_create();
        if (NULL == entry) {
            rc = -3;
            break;
        }

        /** oddly enough, these appear to already be in network order */
        entry->loc_port = ntohs(inpcb.inp_lport);
        entry->rmt_port = ntohs(inpcb.inp_fport);
        entry->pid = 0;
        
        /** the addr string may need work */
	if (inpcb.inp_flags & INP_IPV6) {
	    entry->loc_addr_len = entry->rmt_addr_len = 16;
	    memcpy(entry->loc_addr, &inpcb.inp_laddr6, 16);
	    memcpy(entry->rmt_addr, &inpcb.inp_faddr6, 16);
	}
	else {
	    entry->loc_addr_len = entry->rmt_addr_len = 4;
	    memcpy(entry->loc_addr, &inpcb.inp_laddr, 4);
	    memcpy(entry->rmt_addr, &inpcb.inp_faddr, 4);
	}

        /*
         * add entry to container
         */
        entry->index = CONTAINER_SIZE(container) + 1;
        CONTAINER_INSERT(container, entry);
#if !defined(NETSNMP_ENABLE_IPV6)
    skip:
#endif
	if (next == head)
	    break;
    }

    if (rc < 0)
        return rc;

    return 0;
}