diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-01-07 21:50:53 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1999-01-07 21:50:53 +0000 |
commit | 3c958f7fcf2f17371277f41f1879fdb74833be2f (patch) | |
tree | f5b8bf42433a90e8bb21210945c321650a09a15c /sys/netinet | |
parent | f716a06692c59e52d26cd120f07cffb3b95570de (diff) |
INET6 support
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/in_pcb.c | 233 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 78 |
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 *)); |