summaryrefslogtreecommitdiff
path: root/sys/netinet6/in6_pcb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/in6_pcb.c')
-rw-r--r--sys/netinet6/in6_pcb.c229
1 files changed, 182 insertions, 47 deletions
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 803729e1441..a1c19967147 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_pcb.c,v 1.104 2018/06/14 17:00:58 bluhm Exp $ */
+/* $OpenBSD: in6_pcb.c,v 1.105 2018/09/11 14:34:49 bluhm Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -99,30 +99,25 @@
*
*/
+#include "pf.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
-#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
-#include <sys/errno.h>
-#include <sys/time.h>
-#include <sys/proc.h>
-#include <sys/pledge.h>
#include <net/if.h>
#include <net/if_var.h>
-#include <net/route.h>
+#include <net/pfvar.h>
#include <netinet/in.h>
#include <netinet/ip.h>
-#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
+#include <netinet/in_pcb.h>
#include <netinet6/in6_var.h>
-#include <netinet/ip6.h>
-#include <netinet6/ip6_var.h>
/*
* External globals
@@ -150,6 +145,24 @@ u_char inet6ctlerrmap[PRC_NCMDS] = {
};
#endif
+struct inpcbhead *
+in6_pcbhash(struct inpcbtable *table, int rdom,
+ const struct in6_addr *faddr, u_short fport,
+ const struct in6_addr *laddr, u_short lport)
+{
+ SIPHASH_CTX ctx;
+ u_int32_t nrdom = htonl(rdom);
+
+ SipHash24_Init(&ctx, &table->inpt_key);
+ SipHash24_Update(&ctx, &nrdom, sizeof(nrdom));
+ SipHash24_Update(&ctx, faddr, sizeof(*faddr));
+ SipHash24_Update(&ctx, &fport, sizeof(fport));
+ SipHash24_Update(&ctx, laddr, sizeof(*laddr));
+ SipHash24_Update(&ctx, &lport, sizeof(lport));
+
+ return (&table->inpt_hashtbl[SipHash24_End(&ctx) & table->inpt_mask]);
+}
+
int
in6_pcbaddrisavail(struct inpcb *inp, struct sockaddr_in6 *sin6, int wild,
struct proc *p)
@@ -313,6 +326,52 @@ in6_pcbconnect(struct inpcb *inp, struct mbuf *nam)
}
/*
+ * Get the local address/port, and put it in a sockaddr_in6.
+ * This services the getsockname(2) call.
+ */
+int
+in6_setsockaddr(struct inpcb *inp, struct mbuf *nam)
+{
+ struct sockaddr_in6 *sin6;
+
+ nam->m_len = sizeof(struct sockaddr_in6);
+ sin6 = mtod(nam,struct sockaddr_in6 *);
+
+ bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_port = inp->inp_lport;
+ sin6->sin6_addr = inp->inp_laddr6;
+ /* KAME hack: recover scopeid */
+ in6_recoverscope(sin6, &inp->inp_laddr6);
+
+ return 0;
+}
+
+/*
+ * Get the foreign address/port, and put it in a sockaddr_in6.
+ * This services the getpeername(2) call.
+ */
+int
+in6_setpeeraddr(struct inpcb *inp, struct mbuf *nam)
+{
+ struct sockaddr_in6 *sin6;
+
+ nam->m_len = sizeof(struct sockaddr_in6);
+ sin6 = mtod(nam,struct sockaddr_in6 *);
+
+ bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_port = inp->inp_fport;
+ sin6->sin6_addr = inp->inp_faddr6;
+ /* KAME hack: recover scopeid */
+ in6_recoverscope(sin6, &inp->inp_faddr6);
+
+ 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
@@ -455,48 +514,124 @@ in6_pcbnotify(struct inpcbtable *table, struct sockaddr_in6 *dst,
return (nmatch);
}
-/*
- * Get the local address/port, and put it in a sockaddr_in6.
- * This services the getsockname(2) call.
- */
-int
-in6_setsockaddr(struct inpcb *inp, struct mbuf *nam)
+struct inpcb *
+in6_pcbhashlookup(struct inpcbtable *table, const struct in6_addr *faddr,
+ u_int fport_arg, const struct in6_addr *laddr, u_int lport_arg,
+ u_int rtable)
{
- struct sockaddr_in6 *sin6;
-
- nam->m_len = sizeof(struct sockaddr_in6);
- sin6 = mtod(nam,struct sockaddr_in6 *);
-
- bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(struct sockaddr_in6);
- sin6->sin6_port = inp->inp_lport;
- sin6->sin6_addr = inp->inp_laddr6;
- /* KAME hack: recover scopeid */
- in6_recoverscope(sin6, &inp->inp_laddr6);
+ struct inpcbhead *head;
+ struct inpcb *inp;
+ u_int16_t fport = fport_arg, lport = lport_arg;
+ u_int rdomain;
- return 0;
+ rdomain = rtable_l2(rtable);
+ head = in6_pcbhash(table, rdomain, faddr, fport, laddr, lport);
+ LIST_FOREACH(inp, head, inp_hash) {
+ if (!(inp->inp_flags & INP_IPV6))
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, faddr) &&
+ inp->inp_fport == fport && inp->inp_lport == lport &&
+ IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr) &&
+ rtable_l2(inp->inp_rtableid) == rdomain) {
+ /*
+ * Move this PCB to the head of hash chain so that
+ * repeated accesses are quicker. This is analogous to
+ * the historic single-entry PCB cache.
+ */
+ if (inp != LIST_FIRST(head)) {
+ LIST_REMOVE(inp, inp_hash);
+ LIST_INSERT_HEAD(head, inp, inp_hash);
+ }
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (inp == NULL && in_pcbnotifymiss) {
+ printf("%s: faddr= fport=%d laddr= lport=%d rdom=%u\n",
+ __func__, ntohs(fport), ntohs(lport), rdomain);
+ }
+#endif
+ return (inp);
}
-/*
- * Get the foreign address/port, and put it in a sockaddr_in6.
- * This services the getpeername(2) call.
- */
-int
-in6_setpeeraddr(struct inpcb *inp, struct mbuf *nam)
+struct inpcb *
+in6_pcblookup_listen(struct inpcbtable *table, struct in6_addr *laddr,
+ u_int lport_arg, struct mbuf *m, u_int rtable)
{
- struct sockaddr_in6 *sin6;
-
- nam->m_len = sizeof(struct sockaddr_in6);
- sin6 = mtod(nam,struct sockaddr_in6 *);
+ struct inpcbhead *head;
+ const struct in6_addr *key1, *key2;
+ struct inpcb *inp;
+ u_int16_t lport = lport_arg;
+ u_int rdomain;
- bzero ((caddr_t)sin6,sizeof(struct sockaddr_in6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(struct sockaddr_in6);
- sin6->sin6_port = inp->inp_fport;
- sin6->sin6_addr = inp->inp_faddr6;
- /* KAME hack: recover scopeid */
- in6_recoverscope(sin6, &inp->inp_faddr6);
+ key1 = laddr;
+ key2 = &zeroin6_addr;
+#if NPF > 0
+ if (m && m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
+ struct pf_divert *divert;
+
+ divert = pf_find_divert(m);
+ KASSERT(divert != NULL);
+ switch (divert->type) {
+ case PF_DIVERT_TO:
+ key1 = key2 = &divert->addr.v6;
+ lport = divert->port;
+ break;
+ case PF_DIVERT_REPLY:
+ return (NULL);
+ default:
+ panic("%s: unknown divert type %d, mbuf %p, divert %p",
+ __func__, divert->type, m, divert);
+ }
+ } else if (m && m->m_pkthdr.pf.flags & PF_TAG_TRANSLATE_LOCALHOST) {
+ /*
+ * Redirected connections should not be treated the same
+ * as connections directed to ::1 since localhost
+ * can only be accessed from the host itself.
+ */
+ key1 = &zeroin6_addr;
+ key2 = laddr;
+ }
+#endif
- return 0;
+ rdomain = rtable_l2(rtable);
+ head = in6_pcbhash(table, rdomain, &zeroin6_addr, 0, key1, lport);
+ LIST_FOREACH(inp, head, inp_hash) {
+ if (!(inp->inp_flags & INP_IPV6))
+ continue;
+ if (inp->inp_lport == lport && inp->inp_fport == 0 &&
+ IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key1) &&
+ IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6) &&
+ rtable_l2(inp->inp_rtableid) == rdomain)
+ break;
+ }
+ if (inp == NULL && ! IN6_ARE_ADDR_EQUAL(key1, key2)) {
+ head = in6_pcbhash(table, rdomain,
+ &zeroin6_addr, 0, key2, lport);
+ LIST_FOREACH(inp, head, inp_hash) {
+ if (!(inp->inp_flags & INP_IPV6))
+ continue;
+ if (inp->inp_lport == lport && inp->inp_fport == 0 &&
+ IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key2) &&
+ IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6) &&
+ rtable_l2(inp->inp_rtableid) == rdomain)
+ break;
+ }
+ }
+ /*
+ * Move this PCB to the head of hash chain so that
+ * repeated accesses are quicker. This is analogous to
+ * the historic single-entry PCB cache.
+ */
+ if (inp != NULL && inp != LIST_FIRST(head)) {
+ LIST_REMOVE(inp, inp_hash);
+ LIST_INSERT_HEAD(head, inp, inp_hash);
+ }
+#ifdef DIAGNOSTIC
+ if (inp == NULL && in_pcbnotifymiss) {
+ printf("%s: laddr= lport=%d rdom=%u\n",
+ __func__, ntohs(lport), rdomain);
+ }
+#endif
+ return (inp);
}