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); }
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); } }
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; }
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; }
/** * * @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; }
/* * 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); }
/* * 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; }
/** * * @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; }