summaryrefslogtreecommitdiff
path: root/sys/netinet
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1999-01-07 21:50:53 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1999-01-07 21:50:53 +0000
commit3c958f7fcf2f17371277f41f1879fdb74833be2f (patch)
treef5b8bf42433a90e8bb21210945c321650a09a15c /sys/netinet
parentf716a06692c59e52d26cd120f07cffb3b95570de (diff)
INET6 support
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/in_pcb.c233
-rw-r--r--sys/netinet/in_pcb.h78
2 files changed, 276 insertions, 35 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 37c41804e46..e154baec143 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.c,v 1.26 1999/01/07 06:05:04 deraadt Exp $ */
+/* $OpenBSD: in_pcb.c,v 1.27 1999/01/07 21:50:51 deraadt Exp $ */
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
/*
@@ -36,6 +36,18 @@
* @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
*/
+/*
+%%% portions-copyright-nrl-95
+Portions of this software are Copyright 1995-1998 by Randall Atkinson,
+Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights
+Reserved. All rights under this copyright have been assigned to the US
+Naval Research Laboratory (NRL). The NRL Copyright Notice and License
+Agreement Version 1.1 (January 17, 1995) applies to these portions of the
+software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+*/
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
@@ -82,7 +94,13 @@ int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 40000 */
int ipport_hilastauto = IPPORT_HILASTAUTO; /* 44999 */
#define INPCBHASH(table, faddr, fport, laddr, lport) \
- &(table)->inpt_hashtbl[(ntohl((faddr)->s_addr) + ntohs((fport)) + ntohs((lport))) & (table->inpt_hash)]
+ &(table)->inpt_hashtbl[(ntohl((faddr)->s_addr) + \
+ ntohs((fport)) + ntohs((lport))) & (table->inpt_hash)]
+
+#define IN6PCBHASH(table, faddr, fport, laddr, lport) \
+ &(table)->inpt_hashtbl[(ntohl((faddr)->in6a_words[0] ^ \
+ (faddr)->in6a_words[3]) + ntohs((fport)) + ntohs((lport))) & \
+ (table->inpt_hash)]
void
in_pcbinit(table, hashsize)
@@ -143,6 +161,17 @@ in_pcballoc(so, v)
&inp->inp_laddr, inp->inp_lport), inp, inp_hash);
splx(s);
so->so_pcb = inp;
+
+#ifdef INET6
+ /*
+ * Small change in this function to set the INP_IPV6 flag so routines
+ * outside pcb-specific routines don't need to use sotopf(), and all
+ * of it's pointer chasing, later.
+ */
+ if (sotopf(so) == PF_INET6)
+ inp->inp_flags = INP_IPV6;
+ inp->inp_csumoffset = -1;
+#endif /* INET6 */
return (0);
}
@@ -161,6 +190,11 @@ in_pcbbind(v, nam)
int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
int error;
+#ifdef INET6
+ if (sotopf(so) == PF_INET6)
+ return in6_pcbbind(inp, nam);
+#endif /* INET6 */
+
if (in_ifaddr.tqh_first == 0)
return (EADDRNOTAVAIL);
if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
@@ -320,6 +354,12 @@ in_pcbconnect(v, nam)
struct in_ifaddr *ia;
struct sockaddr_in *ifaddr = NULL;
register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+ struct sockaddr_in *sin2;
+
+#ifdef INET6
+ if (sotopf(inp->inp_socket) == PF_INET6)
+ return (in6_pcbconnect(inp, nam));
+#endif /* INET6 */
if (nam->m_len != sizeof (*sin))
return (EINVAL);
@@ -365,6 +405,13 @@ in_pcbconnect(v, nam)
ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
satosin(&ro->ro_dst)->sin_addr = sin->sin_addr;
rtalloc(ro);
+
+ /*
+ * It is important to bzero out the rest of the
+ * struct sockaddr_in when mixing v6 & v4!
+ */
+ sin2 = (struct sockaddr_in *)&ro->ro_dst;
+ bzero(sin2->sin_zero, sizeof(sin2->sin_zero));
}
/*
* If we found a route, use the address
@@ -393,7 +440,12 @@ in_pcbconnect(v, nam)
* address of that interface as our source address.
*/
if (IN_MULTICAST(sin->sin_addr.s_addr) &&
+#ifdef INET6
+ inp->inp_moptions != NULL &&
+ !(inp->inp_flags & INP_IPV6_MCAST)) {
+#else
inp->inp_moptions != NULL) {
+#endif
struct ip_moptions *imo;
struct ifnet *ifp;
@@ -436,7 +488,16 @@ in_pcbdisconnect(v)
{
struct inpcb *inp = v;
- inp->inp_faddr.s_addr = INADDR_ANY;
+#ifdef INET6
+ if (sotopf(inp->inp_socket) == PF_INET6) {
+ DPRINTF(IDL_FINISHED,("In INET6 disconnect!"));
+ inp->inp_faddr6 = in6addr_any;
+ /* Disconnected AF_INET6 sockets cannot be "v4-mapped" */
+ inp->inp_flags &= ~INP_IPV6_MAPPED;
+ } else
+#endif
+ inp->inp_faddr.s_addr = INADDR_ANY;
+
inp->inp_fport = 0;
in_pcbrehash(inp);
if (inp->inp_socket->so_state & SS_NOFDREF)
@@ -454,10 +515,15 @@ in_pcbdetach(v)
so->so_pcb = 0;
sofree(so);
if (inp->inp_options)
- (void)m_free(inp->inp_options);
+ (void)m_freem(inp->inp_options);
if (inp->inp_route.ro_rt)
rtfree(inp->inp_route.ro_rt);
- ip_freemoptions(inp->inp_moptions);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6_MCAST)
+ ipv6_freemoptions(inp->inp_moptions6);
+ else
+#endif /* INET6 */
+ ip_freemoptions(inp->inp_moptions);
#ifdef IPSEC
/* XXX IPsec cleanup here */
#endif
@@ -491,6 +557,11 @@ in_setpeeraddr(inp, nam)
{
register struct sockaddr_in *sin;
+#ifdef INET6
+ if (sotopf(inp->inp_socket) == PF_INET6)
+ in6_setpeeraddr(inp, nam);
+#endif /* INET6 */
+
nam->m_len = sizeof (*sin);
sin = mtod(nam, struct sockaddr_in *);
bzero((caddr_t)sin, sizeof (*sin));
@@ -524,6 +595,14 @@ in_pcbnotify(table, dst, fport_arg, laddr, lport_arg, errno, notify)
struct in_addr faddr;
u_int16_t fport = fport_arg, lport = lport_arg;
+#ifdef INET6
+ /*
+ * See in6_pcbnotify() for IPv6 codepath. By the time this
+ * gets called, the addresses passed are either definitely IPv4 or
+ * IPv6; *_pcbnotify() never gets called with v4-mapped v6 addresses.
+ */
+#endif /* INET6 */
+
if (dst->sa_family != AF_INET)
return;
faddr = satosin(dst)->sin_addr;
@@ -557,6 +636,14 @@ in_pcbnotifyall(table, dst, errno, notify)
register struct inpcb *inp, *oinp;
struct in_addr faddr;
+#ifdef INET6
+ /*
+ * See in6_pcbnotify() for IPv6 codepath. By the time this
+ * gets called, the addresses passed are either definitely IPv4 or
+ * IPv6; *_pcbnotify() never gets called with v4-mapped v6 addresses.
+ */
+#endif /* INET6 */
+
if (dst->sa_family != AF_INET)
return;
faddr = satosin(dst)->sin_addr;
@@ -648,25 +735,66 @@ in_pcblookup(table, faddrp, fport_arg, laddrp, lport_arg, flags)
if (inp->inp_lport != lport)
continue;
wildcard = 0;
- if (inp->inp_faddr.s_addr != INADDR_ANY) {
- if (faddr.s_addr == INADDR_ANY)
- wildcard++;
- else if (inp->inp_faddr.s_addr != faddr.s_addr ||
- inp->inp_fport != fport)
- continue;
- } else {
- if (faddr.s_addr != INADDR_ANY)
- wildcard++;
- }
- if (inp->inp_laddr.s_addr != INADDR_ANY) {
- if (laddr.s_addr == INADDR_ANY)
- wildcard++;
- else if (inp->inp_laddr.s_addr != laddr.s_addr)
+#ifdef INET6
+ if (flags & INPLOOKUP_IPV6) {
+ struct in6_addr *laddr6 = (struct in6_addr *)laddr_arg;
+ struct in6_addr *faddr6 = (struct in6_addr *)faddr_arg;
+
+ /*
+ * Always skip AF_INET sockets when looking for AF_INET6
+ * addresses. The only problem with this comes if the
+ * PF_INET6 addresses are v4-mapped addresses. From what
+ * I've been able to see, none of the callers cause such
+ * a situation to occur. If such a situation DID occur,
+ * then it is possible to miss a matching PCB.
+ */
+ if (!(inp->inp_flags & INP_IPV6))
continue;
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(laddr6))
+ wildcard++;
+ else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr6))
+ continue;
+ } else {
+ if (!IN6_IS_ADDR_UNSPECIFIED(laddr6))
+ wildcard++;
+ }
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(faddr6))
+ wildcard++;
+ else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6,
+ faddr6) || inp->inp_fport != fport)
+ continue;
+ } else {
+ if (!IN6_IS_ADDR_UNSPECIFIED(faddr6))
+ wildcard++;
+ }
} else {
- if (laddr.s_addr != INADDR_ANY)
- wildcard++;
+#endif /* INET6 */
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (faddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_fport != fport)
+ continue;
+ } else {
+ if (faddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (laddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_laddr.s_addr != laddr.s_addr)
+ continue;
+ } else {
+ if (laddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+#ifdef INET6
}
+#endif /* INET6 */
if ((!wildcard || (flags & INPLOOKUP_WILDCARD)) &&
wildcard < matchwild) {
match = inp;
@@ -686,8 +814,19 @@ in_pcbrehash(inp)
s = splnet();
LIST_REMOVE(inp, inp_hash);
- LIST_INSERT_HEAD(INPCBHASH(table, &inp->inp_faddr, inp->inp_fport,
- &inp->inp_laddr, inp->inp_lport), inp, inp_hash);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6) {
+ LIST_INSERT_HEAD(IN6PCBHASH(table, &inp->inp_faddr6,
+ inp->inp_fport, &inp->inp_laddr6, inp->inp_lport),
+ inp, inp_hash);
+ } else {
+#endif /* INET6 */
+ LIST_INSERT_HEAD(INPCBHASH(table, &inp->inp_faddr,
+ inp->inp_fport, &inp->inp_laddr, inp->inp_lport),
+ inp, inp_hash);
+#ifdef INET6
+ }
+#endif /* INET6 */
splx(s);
}
@@ -732,3 +871,51 @@ in_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
#endif
return (inp);
}
+
+#ifdef INET6
+struct inpcb *
+in6_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
+ struct inpcbtable *table;
+ struct in6_addr *faddr, *laddr;
+ u_int fport_arg, lport_arg;
+{
+ struct inpcbhead *head;
+ register struct inpcb *inp;
+ u_int16_t fport = fport_arg, lport = lport_arg;
+
+ DPRINTF(IDL_FINISHED,
+ ("in6_pcbhashlookup(table=%08x, faddr=%08x, fport_arg=%x, "
+ "laddr=%08x, lport_arg=%x)\n", OSDEP_PCAST(table),
+ OSDEP_PCAST(faddr), fport_arg, OSDEP_PCAST(laddr), lport_arg));
+
+ head = IN6PCBHASH(table, faddr, fport, laddr, lport);
+ for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
+ 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)) {
+ /*
+ * 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 != head->lh_first) {
+ LIST_REMOVE(inp, inp_hash);
+ LIST_INSERT_HEAD(head, inp, inp_hash);
+ }
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (inp == NULL && in_pcbnotifymiss) {
+ printf("in6_pcblookup_connect: faddr=");
+ DDO(IDL_MAJOR_EVENT, dump_in6_addr(faddr));
+ printf(" fport=%d laddr=", ntohs(fport));
+ DDO(IDL_MAJOR_EVENT, dump_in6_addr(laddr));
+ printf(" lport=%d\n", ntohs(lport));
+ }
+#endif
+ return (inp);
+}
+#endif /* INET6 */
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 9aec9b0c74f..6e2742c639f 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.h,v 1.12 1999/01/07 06:05:04 deraadt Exp $ */
+/* $OpenBSD: in_pcb.h,v 1.13 1999/01/07 21:50:52 deraadt Exp $ */
/* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */
/*
@@ -37,6 +37,18 @@
*/
#include <sys/queue.h>
+#include <netinet6/in6.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/icmpv6.h>
+
+union inpaddru {
+ struct in6_addr iau_addr6;
+ struct {
+ uint8_t pad[12];
+ struct in_addr inaddr; /* easier transition */
+ } iau_a4u;
+};
/*
* Common structure pcb for internet protocol implementation.
@@ -49,15 +61,29 @@ struct inpcb {
LIST_ENTRY(inpcb) inp_hash;
CIRCLEQ_ENTRY(inpcb) inp_queue;
struct inpcbtable *inp_table;
- struct in_addr inp_faddr; /* foreign host table entry */
- struct in_addr inp_laddr; /* local host table entry */
+ union inpaddru inp_faddru; /* Foreign address. */
+ union inpaddru inp_laddru; /* Local address. */
+#define inp_faddr inp_faddru.iau_a4u.inaddr
+#define inp_faddr6 inp_faddru.iau_addr6
+#define inp_laddr inp_laddru.iau_a4u.inaddr
+#define inp_laddr6 inp_laddru.iau_addr6
u_int16_t inp_fport; /* foreign port */
u_int16_t inp_lport; /* local port */
struct socket *inp_socket; /* back pointer to socket */
caddr_t inp_ppcb; /* pointer to per-protocol pcb */
- struct route inp_route; /* placeholder for routing entry */
+ union { /* Route (notice increased size). */
+ struct route ru_route;
+ struct route6 ru_route6;
+ } inp_ru;
+#define inp_route inp_ru.ru_route
+#define inp_route6 inp_ru.ru_route6
int inp_flags; /* generic IP/datagram flags */
- struct ip inp_ip; /* header prototype; should have more */
+ union { /* Header prototype. */
+ struct ip hu_ip;
+ struct ipv6 hu_ipv6;
+ } inp_hu;
+#define inp_ip inp_hu.hu_ip
+#define inp_ipv6 inp_hu.hu_ipv6
struct mbuf *inp_options; /* IP options */
struct ip_moptions *inp_moptions; /* IP multicast options */
u_char inp_seclevel[3]; /* Only the first 3 are used for now */
@@ -69,6 +95,9 @@ struct inpcb {
#define SR_FAILED 1 /* Negotiation failed permanently */
#define SR_SUCCESS 2 /* SA successfully established */
#define SR_WAIT 3 /* Waiting for SA */
+ int inp_fflowinfo; /* Foreign flowlabel & priority */
+ int inp_csumoffset;
+ struct icmpv6_filter inp_filter;
};
struct inpcbtable {
@@ -79,16 +108,38 @@ struct inpcbtable {
};
/* flags in inp_flags: */
-#define INP_RECVOPTS 0x01 /* receive incoming IP options */
-#define INP_RECVRETOPTS 0x02 /* receive IP options for reply */
-#define INP_RECVDSTADDR 0x04 /* receive IP dst address */
-#define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR)
-#define INP_HDRINCL 0x08 /* user supplies entire IP header */
-#define INP_HIGHPORT 0x10 /* user wants "high" port binding */
-#define INP_LOWPORT 0x20 /* user wants "low" port binding */
+#define INP_RECVOPTS 0x001 /* receive incoming IP options */
+#define INP_RECVRETOPTS 0x002 /* receive IP options for reply */
+#define INP_RECVDSTADDR 0x004 /* receive IP dst address */
+
+#define INP_RXDSTOPTS INP_RECVOPTS
+#define INP_RXHOPOPTS INP_RECVRETOPTS
+#define INP_RXINFO INP_RECVDSTADDR
+#define INP_RXSRCRT 0x010
+#define INP_HOPLIMIT 0x020
+
+#define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR| \
+ INP_RXSRCRT|INP_HOPLIMIT)
+
+#define INP_HDRINCL 0x008 /* user supplies entire IP header */
+#define INP_HIGHPORT 0x010 /* user wants "high" port binding */
+#define INP_LOWPORT 0x020 /* user wants "low" port binding */
+
+/*
+ * These flags' values should be determined by either the transport
+ * protocol at PRU_BIND, PRU_LISTEN, PRU_CONNECT, etc, or by in_pcb*().
+ */
+#define INP_IPV6 0x100 /* sotopf(inp->inp_socket) == PF_INET6 */
+#define INP_IPV6_UNDEC 0x200 /* PCB is PF_INET6, but listens for V4/V6 */
+#define INP_IPV6_MAPPED 0x400 /* PF_INET6 PCB which is connected to
+ * an IPv4 host, or is bound to
+ * an IPv4 address (specified with
+ * the mapped form of v6 addresses) */
+#define INP_IPV6_MCAST 0x800 /* Set if inp_moptions points to ipv6 ones */
#define INPLOOKUP_WILDCARD 1
#define INPLOOKUP_SETLOCAL 2
+#define INPLOOKUP_IPV6 4
#define sotoinpcb(so) ((struct inpcb *)(so)->so_pcb)
@@ -109,6 +160,9 @@ struct baddynamicports {
};
#ifdef _KERNEL
+
+#define sotopf(so) (so->so_proto->pr_domain->dom_family)
+
void in_losing __P((struct inpcb *));
int in_pcballoc __P((struct socket *, void *));
int in_pcbbind __P((void *, struct mbuf *));