From cad2014b2749528351ec5180e88a5929efebbfc4 Mon Sep 17 00:00:00 2001 From: shin Date: Mon, 22 Nov 1999 02:45:11 +0000 Subject: KAME netinet6 basic part(no IPsec,no V6 Multicast Forwarding, no UDP/TCP for IPv6 yet) With this patch, you can assigne IPv6 addr automatically, and can reply to IPv6 ping. Reviewed by: freebsd-arch, cvs-committers Obtained from: KAME project --- sys/amd64/conf/GENERIC | 1 + sys/conf/Makefile.i386 | 4 +- sys/conf/Makefile.powerpc | 4 +- sys/conf/files | 20 + sys/conf/options | 18 +- sys/i386/conf/GENERIC | 1 + sys/i386/conf/Makefile.i386 | 4 +- sys/kern/kern_malloc.c | 7 +- sys/kern/uipc_socket.c | 113 ++- sys/net/if.c | 139 ++- sys/net/if_ethersubr.c | 90 +- sys/net/if_gif.h | 71 ++ sys/net/if_loop.c | 53 +- sys/net/if_var.h | 15 +- sys/net/net_osdep.c | 58 ++ sys/net/net_osdep.h | 121 +++ sys/net/pfkeyv2.h | 420 +++++++++ sys/net/route.c | 46 +- sys/netinet/icmp6.h | 37 + sys/netinet/in_pcb.c | 16 +- sys/netinet/in_pcb.h | 151 ++- sys/netinet/ip6.h | 32 + sys/netinet/raw_ip.c | 16 +- sys/netinet/tcp_subr.c | 1 + sys/netinet/tcp_timewait.c | 1 + sys/netinet6/dest6.c | 108 +++ sys/netinet6/frag6.c | 573 ++++++++++++ sys/netinet6/icmp6.c | 1868 +++++++++++++++++++++++++++++++++++++ sys/netinet6/icmp6.h | 602 ++++++++++++ sys/netinet6/in6.c | 1879 +++++++++++++++++++++++++++++++++++++ sys/netinet6/in6.h | 35 +- sys/netinet6/in6_cksum.c | 301 ++++++ sys/netinet6/in6_ifattach.c | 690 ++++++++++++++ sys/netinet6/in6_ifattach.h | 50 + sys/netinet6/in6_pcb.c | 1131 ++++++++++++++++++++++ sys/netinet6/in6_pcb.h | 108 +++ sys/netinet6/in6_prefix.c | 1106 ++++++++++++++++++++++ sys/netinet6/in6_prefix.h | 88 ++ sys/netinet6/in6_proto.c | 410 ++++++++ sys/netinet6/in6_rmx.c | 477 ++++++++++ sys/netinet6/in6_var.h | 101 +- sys/netinet6/ip6.h | 245 +++++ sys/netinet6/ip6_forward.c | 391 ++++++++ sys/netinet6/ip6_fw.h | 202 ++++ sys/netinet6/ip6_input.c | 1016 ++++++++++++++++++++ sys/netinet6/ip6_output.c | 2176 +++++++++++++++++++++++++++++++++++++++++++ sys/netinet6/ip6_var.h | 251 +++++ sys/netinet6/ip6protosw.h | 129 +++ sys/netinet6/ipsec.h | 314 +++++++ sys/netinet6/ipsec6.h | 77 ++ sys/netinet6/mld6.c | 459 +++++++++ sys/netinet6/mld6_var.h | 52 ++ sys/netinet6/nd6.c | 1531 ++++++++++++++++++++++++++++++ sys/netinet6/nd6.h | 302 ++++++ sys/netinet6/nd6_nbr.c | 1123 ++++++++++++++++++++++ sys/netinet6/nd6_rtr.c | 1243 ++++++++++++++++++++++++ sys/netinet6/pim6.h | 68 ++ sys/netinet6/pim6_var.h | 71 ++ sys/netinet6/raw_ip6.c | 612 ++++++++++++ sys/netinet6/route6.c | 155 +++ sys/netinet6/tcp6_var.h | 83 ++ sys/netinet6/udp6_var.h | 80 ++ sys/netkey/key_var.h | 106 +++ sys/netkey/keydb.h | 135 +++ sys/netkey/keysock.h | 56 ++ sys/sys/socketvar.h | 6 + 66 files changed, 21626 insertions(+), 223 deletions(-) create mode 100644 sys/net/if_gif.h create mode 100644 sys/net/net_osdep.c create mode 100644 sys/net/net_osdep.h create mode 100644 sys/net/pfkeyv2.h create mode 100644 sys/netinet/icmp6.h create mode 100644 sys/netinet/ip6.h create mode 100644 sys/netinet6/dest6.c create mode 100644 sys/netinet6/frag6.c create mode 100644 sys/netinet6/icmp6.c create mode 100644 sys/netinet6/icmp6.h create mode 100644 sys/netinet6/in6.c create mode 100644 sys/netinet6/in6_cksum.c create mode 100644 sys/netinet6/in6_ifattach.c create mode 100644 sys/netinet6/in6_ifattach.h create mode 100644 sys/netinet6/in6_pcb.c create mode 100644 sys/netinet6/in6_pcb.h create mode 100644 sys/netinet6/in6_prefix.c create mode 100644 sys/netinet6/in6_prefix.h create mode 100644 sys/netinet6/in6_proto.c create mode 100644 sys/netinet6/in6_rmx.c create mode 100644 sys/netinet6/ip6.h create mode 100644 sys/netinet6/ip6_forward.c create mode 100644 sys/netinet6/ip6_fw.h create mode 100644 sys/netinet6/ip6_input.c create mode 100644 sys/netinet6/ip6_output.c create mode 100644 sys/netinet6/ip6_var.h create mode 100644 sys/netinet6/ip6protosw.h create mode 100644 sys/netinet6/ipsec.h create mode 100644 sys/netinet6/ipsec6.h create mode 100644 sys/netinet6/mld6.c create mode 100644 sys/netinet6/mld6_var.h create mode 100644 sys/netinet6/nd6.c create mode 100644 sys/netinet6/nd6.h create mode 100644 sys/netinet6/nd6_nbr.c create mode 100644 sys/netinet6/nd6_rtr.c create mode 100644 sys/netinet6/pim6.h create mode 100644 sys/netinet6/pim6_var.h create mode 100644 sys/netinet6/raw_ip6.c create mode 100644 sys/netinet6/route6.c create mode 100644 sys/netinet6/tcp6_var.h create mode 100644 sys/netinet6/udp6_var.h create mode 100644 sys/netkey/key_var.h create mode 100644 sys/netkey/keydb.h create mode 100644 sys/netkey/keysock.h diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 60c5e9a..2c04a17 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -29,6 +29,7 @@ maxusers 32 options MATH_EMULATE #Support for x87 emulation options INET #InterNETworking +#options "INET6" #IPv6 options FFS #Berkeley Fast Filesystem options FFS_ROOT #FFS usable as root device [keep this!] options MFS #Memory Filesystem diff --git a/sys/conf/Makefile.i386 b/sys/conf/Makefile.i386 index eca1513..0e5820c 100644 --- a/sys/conf/Makefile.i386 +++ b/sys/conf/Makefile.i386 @@ -41,7 +41,9 @@ INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif -COPTS= ${INCLUDES} ${IDENT} -DKERNEL -include opt_global.h +COPTS= ${INCLUDES} ${IDENT} -DKERNEL -include opt_global.h +# KAME mandatory flags +COPTS+= -D_KERNEL CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # XXX LOCORE means "don't declare C stuff" not "for locore.s". diff --git a/sys/conf/Makefile.powerpc b/sys/conf/Makefile.powerpc index eca1513..0e5820c 100644 --- a/sys/conf/Makefile.powerpc +++ b/sys/conf/Makefile.powerpc @@ -41,7 +41,9 @@ INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif -COPTS= ${INCLUDES} ${IDENT} -DKERNEL -include opt_global.h +COPTS= ${INCLUDES} ${IDENT} -DKERNEL -include opt_global.h +# KAME mandatory flags +COPTS+= -D_KERNEL CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # XXX LOCORE means "don't declare C stuff" not "for locore.s". diff --git a/sys/conf/files b/sys/conf/files index b57b927..f658a65 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -483,6 +483,7 @@ net/rtsock.c standard net/slcompress.c optional ppp net/slcompress.c optional sl net/zlib.c optional ppp_deflate +net/net_osdep.c standard netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk netatalk/at_proto.c optional netatalk @@ -612,6 +613,25 @@ netinet/tcp_subr.c optional inet netinet/tcp_timer.c optional inet netinet/tcp_usrreq.c optional inet netinet/udp_usrreq.c optional inet +netinet6/in6.c optional inet6 +netinet6/in6_ifattach.c optional inet6 +netinet6/in6_cksum.c optional inet6 +netinet6/in6_pcb.c optional inet6 +netinet6/in6_proto.c optional inet6 +netinet6/in6_rmx.c optional inet6 +netinet6/in6_prefix.c optional inet6 +netinet6/dest6.c optional inet6 +netinet6/frag6.c optional inet6 +netinet6/icmp6.c optional inet6 +netinet6/ip6_input.c optional inet6 +netinet6/ip6_forward.c optional inet6 +netinet6/ip6_output.c optional inet6 +netinet6/route6.c optional inet6 +netinet6/mld6.c optional inet6 +netinet6/nd6.c optional inet6 +netinet6/nd6_nbr.c optional inet6 +netinet6/nd6_rtr.c optional inet6 +netinet6/raw_ip6.c optional inet6 netipx/ipx.c optional ipx netipx/ipx_cksum.c optional ipx netipx/ipx_input.c optional ipx diff --git a/sys/conf/options b/sys/conf/options index 33cdfbe..33a5066 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -1,7 +1,7 @@ # $FreeBSD$ # # On the handling of kernel options -# +# # All kernel options should be listed in LINT, with suitable # descriptions. Negative options (options that make some code not # compile) should be commented out; LINT should compile as much code @@ -10,7 +10,7 @@ # possible to have a full compile-test. If necessary, you can include # "opt_lint.h" and check for COMPILING_LINT to get maximum code # coverage. -# +# # All new options shall also be listed in either "conf/options" or # "/conf/options.". Options that affect a single # source-file .[c|s] should be directed into "opt_.h", while @@ -20,7 +20,7 @@ # only some files. Note that the effect of listing only an option # without a header-file-name in conf/options (and cousins) is that the # last convention is followed. -# +# # This handling scheme is not yet fully implemented. # # @@ -103,8 +103,8 @@ FFS NFS NWFS -# If you are following the conditions in the copyright, -# you can enable soft-updates which will speed up a lot of thigs +# If you are following the conditions in the copyright, +# you can enable soft-updates which will speed up a lot of thigs # and make the system safer from crashes at the same time. # otherwise a STUB module will be compiled in. SOFTUPDATES opt_ffs.h @@ -216,6 +216,10 @@ BRIDGE opt_bdg.h MROUTING opt_mrouting.h INET opt_inet.h INET6 opt_inet.h +IPSEC opt_ipsec.h +IPSEC_ESP opt_ipsec.h +IPSEC_DEBUG opt_ipsec.h +IPSEC_IPV6FWD opt_ipsec.h IPDIVERT DUMMYNET opt_ipdn.h IPFILTER_LKM opt_ipfilter.h @@ -224,6 +228,10 @@ IPFIREWALL_VERBOSE opt_ipfw.h IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPFIREWALL_DEFAULT_TO_ACCEPT opt_ipfw.h IPFIREWALL_FORWARD opt_ipfw.h +IPV6FIREWALL opt_ip6fw.h +IPV6FIREWALL_VERBOSE opt_ip6fw.h +IPV6FIREWALL_VERBOSE_LIMIT opt_ip6fw.h +IPV6FIREWALL_DEFAULT_TO_ACCEPT opt_ip6fw.h IPSTEALTH IPX opt_ipx.h IPXIP opt_ipx.h diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 60c5e9a..2c04a17 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -29,6 +29,7 @@ maxusers 32 options MATH_EMULATE #Support for x87 emulation options INET #InterNETworking +#options "INET6" #IPv6 options FFS #Berkeley Fast Filesystem options FFS_ROOT #FFS usable as root device [keep this!] options MFS #Memory Filesystem diff --git a/sys/i386/conf/Makefile.i386 b/sys/i386/conf/Makefile.i386 index eca1513..0e5820c 100644 --- a/sys/i386/conf/Makefile.i386 +++ b/sys/i386/conf/Makefile.i386 @@ -41,7 +41,9 @@ INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif -COPTS= ${INCLUDES} ${IDENT} -DKERNEL -include opt_global.h +COPTS= ${INCLUDES} ${IDENT} -DKERNEL -include opt_global.h +# KAME mandatory flags +COPTS+= -D_KERNEL CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} # XXX LOCORE means "don't declare C stuff" not "for locore.s". diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c index 7d4cce0..c54f9a7 100644 --- a/sys/kern/kern_malloc.c +++ b/sys/kern/kern_malloc.c @@ -59,6 +59,9 @@ MALLOC_DEFINE(M_CACHE, "cache", "Various Dynamically allocated caches"); MALLOC_DEFINE(M_DEVBUF, "devbuf", "device driver memory"); MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers"); +MALLOC_DEFINE(M_IP6OPT, "ip6opt", "IPv6 options"); +MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery"); + static void kmeminit __P((void *)); SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL) @@ -473,7 +476,7 @@ malloc_init(data) { struct malloc_type *type = (struct malloc_type *)data; - if (type->ks_magic != M_MAGIC) + if (type->ks_magic != M_MAGIC) panic("malloc type lacks magic"); if (type->ks_limit != 0) @@ -498,7 +501,7 @@ malloc_uninit(data) struct malloc_type *type = (struct malloc_type *)data; struct malloc_type *t; - if (type->ks_magic != M_MAGIC) + if (type->ks_magic != M_MAGIC) panic("malloc type lacks magic"); if (cnt.v_page_count == 0) diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index c650a36..c6d107c 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -64,7 +64,7 @@ MALLOC_DEFINE(M_PCB, "pcb", "protocol control block"); SYSCTL_DECL(_kern_ipc); static int somaxconn = SOMAXCONN; -SYSCTL_INT(_kern_ipc, KIPC_SOMAXCONN, somaxconn, CTLFLAG_RW, +SYSCTL_INT(_kern_ipc, KIPC_SOMAXCONN, somaxconn, CTLFLAG_RW, &somaxconn, 0, "Maximum pending socket connection queue size"); /* @@ -1218,6 +1218,117 @@ integer: } } +/* XXX; prepare mbuf for (__FreeBSD__ < 3) routines. */ +int +soopt_getm(struct sockopt *sopt, struct mbuf **mp) +{ + struct mbuf *m, *m_prev; + int sopt_size = sopt->sopt_valsize; + + MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_DATA); + if (m == 0) + return ENOBUFS; + if (sopt_size > MLEN) { + MCLGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + return ENOBUFS; + } + m->m_len = min(MCLBYTES, sopt_size); + } else { + m->m_len = min(MLEN, sopt_size); + } + sopt_size -= m->m_len; + *mp = m; + m_prev = m; + + while (sopt_size) { + MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(*mp); + return ENOBUFS; + } + if (sopt_size > MLEN) { + MCLGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(*mp); + return ENOBUFS; + } + m->m_len = min(MCLBYTES, sopt_size); + } else { + m->m_len = min(MLEN, sopt_size); + } + sopt_size -= m->m_len; + m_prev->m_next = m; + m_prev = m; + } + return 0; +} + +/* XXX; copyin sopt data into mbuf chain for (__FreeBSD__ < 3) routines. */ +int +soopt_mcopyin(struct sockopt *sopt, struct mbuf *m) +{ + struct mbuf *m0 = m; + + if (sopt->sopt_val == NULL) + return 0; + while (m != NULL && sopt->sopt_valsize >= m->m_len) { + if (sopt->sopt_p != NULL) { + int error; + + error = copyin(sopt->sopt_val, mtod(m, char *), + m->m_len); + if (error != 0) { + m_freem(m0); + return(error); + } + } else + bcopy(sopt->sopt_val, mtod(m, char *), m->m_len); + sopt->sopt_valsize -= m->m_len; + (caddr_t)sopt->sopt_val += m->m_len; + m = m->m_next; + } + if (m != NULL) /* should be allocated enoughly at ip6_sooptmcopyin() */ + panic("ip6_sooptmcopyin"); + return 0; +} + +/* XXX; copyout mbuf chain data into soopt for (__FreeBSD__ < 3) routines. */ +int +soopt_mcopyout(struct sockopt *sopt, struct mbuf *m) +{ + struct mbuf *m0 = m; + size_t valsize = 0; + + if (sopt->sopt_val == NULL) + return 0; + while (m != NULL && sopt->sopt_valsize >= m->m_len) { + if (sopt->sopt_p != NULL) { + int error; + + error = copyout(mtod(m, char *), sopt->sopt_val, + m->m_len); + if (error != 0) { + m_freem(m0); + return(error); + } + } else + bcopy(mtod(m, char *), sopt->sopt_val, m->m_len); + sopt->sopt_valsize -= m->m_len; + (caddr_t)sopt->sopt_val += m->m_len; + valsize += m->m_len; + m = m->m_next; + } + if (m != NULL) { + /* enough soopt buffer should be given from user-land */ + m_freem(m0); + return(EINVAL); + } + sopt->sopt_valsize = valsize; + return 0; +} + void sohasoutofband(so) register struct socket *so; diff --git a/sys/net/if.c b/sys/net/if.c index 37d3008..7c560bc 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -35,6 +35,7 @@ */ #include "opt_compat.h" +#include "opt_inet.h" #include #include @@ -53,6 +54,11 @@ #include #include +#ifdef INET6 +/*XXX*/ +#include +#endif + /* * System initialization */ @@ -71,6 +77,14 @@ MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); int ifqmaxlen = IFQ_MAXLEN; struct ifnethead ifnet; /* depend on static init XXX */ +#ifdef INET6 +/* + * XXX: declare here to avoid to include many inet6 related files.. + * should be more generalized? + */ +extern void nd6_setmtu __P((struct ifnet *)); +#endif + /* * Network interface utility routines. * @@ -98,6 +112,7 @@ ifinit(dummy) int if_index = 0; struct ifaddr **ifnet_addrs; +struct ifnet **ifindex2ifnet = NULL; /* @@ -131,19 +146,32 @@ if_attach(ifp) * this unlikely case. */ TAILQ_INIT(&ifp->if_addrhead); + TAILQ_INIT(&ifp->if_prefixhead); LIST_INIT(&ifp->if_multiaddrs); getmicrotime(&ifp->if_lastchange); if (ifnet_addrs == 0 || if_index >= if_indexlim) { unsigned n = (if_indexlim <<= 1) * sizeof(ifa); - struct ifaddr **q = (struct ifaddr **) - malloc(n, M_IFADDR, M_WAITOK); - bzero((caddr_t)q, n); + caddr_t q = malloc(n, M_IFADDR, M_WAITOK); + bzero(q, n); if (ifnet_addrs) { bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2); free((caddr_t)ifnet_addrs, M_IFADDR); } - ifnet_addrs = q; + ifnet_addrs = (struct ifaddr **)q; + + /* grow ifindex2ifnet */ + n = if_indexlim * sizeof(struct ifnet *); + q = malloc(n, M_IFADDR, M_WAITOK); + bzero(q, n); + if (ifindex2ifnet) { + bcopy((caddr_t)ifindex2ifnet, q, n/2); + free((caddr_t)ifindex2ifnet, M_IFADDR); + } + ifindex2ifnet = (struct ifnet **)q; } + + ifindex2ifnet[if_index] = ifp; + /* * create a Link Level name for this device */ @@ -207,7 +235,7 @@ if_detach(ifp) ifa = TAILQ_FIRST(&ifp->if_addrhead)) { TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); IFAFREE(ifa); - } + } TAILQ_REMOVE(&ifnet, ifp, if_link); } @@ -226,13 +254,15 @@ ifa_ifwithaddr(addr) #define equal(a1, a2) \ (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) - for (ifa = ifp->if_addrhead.tqh_first; ifa; + for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (equal(addr, ifa->ifa_addr)) return (ifa); if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && + /* IP6 doesn't have broadcast */ + ifa->ifa_broadaddr->sa_len != 0 && equal(ifa->ifa_broadaddr, addr)) return (ifa); } @@ -251,7 +281,7 @@ ifa_ifwithdstaddr(addr) for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) if (ifp->if_flags & IFF_POINTOPOINT) - for (ifa = ifp->if_addrhead.tqh_first; ifa; + for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; @@ -285,7 +315,7 @@ ifa_ifwithnet(addr) return (ifnet_addrs[sdl->sdl_index - 1]); } - /* + /* * Scan though each interface, looking for ones that have * addresses in this address family. */ @@ -296,13 +326,17 @@ ifa_ifwithnet(addr) if (ifa->ifa_addr->sa_family != af) next: continue; - if (ifp->if_flags & IFF_POINTOPOINT) { + if ( +#ifdef INET6 /* XXX: for maching gif tunnel dst as routing entry gateway */ + addr->sa_family != AF_INET6 && +#endif + ifp->if_flags & IFF_POINTOPOINT) { /* - * This is a bit broken as it doesn't - * take into account that the remote end may + * This is a bit broken as it doesn't + * take into account that the remote end may * be a single node in the network we are * looking for. - * The trouble is that we don't know the + * The trouble is that we don't know the * netmask for the remote end. */ if (ifa->ifa_dstaddr != 0 @@ -372,7 +406,7 @@ ifaof_ifpforaddr(addr, ifp) if (af >= AF_MAX) return (0); - for (ifa = ifp->if_addrhead.tqh_first; ifa; + for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { if (ifa->ifa_addr->sa_family != af) continue; @@ -471,6 +505,9 @@ if_route(ifp, flag, fam) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFUP, ifa->ifa_addr); rt_ifmsg(ifp); +#ifdef INET6 + in6_if_up(ifp); +#endif } /* @@ -559,9 +596,9 @@ ifunit(name) /* * Look for a non numeric part */ - end = name + IFNAMSIZ; + end = name + IFNAMSIZ; cp2 = namebuf; - cp = name; + cp = name; while ((cp < end) && (c = *cp)) { if (c >= '0' && c <= '9') break; @@ -576,7 +613,7 @@ ifunit(name) */ len = cp - name + 1; for (unit = 0; - ((c = *cp) >= '0') && (c <= '9') && (unit < 1000000); cp++ ) + ((c = *cp) >= '0') && (c <= '9') && (unit < 1000000); cp++ ) unit = (unit * 10) + (c - '0'); if (*cp != '\0') return 0; /* no trailing garbage allowed */ @@ -592,6 +629,35 @@ ifunit(name) return (ifp); } + +/* + * Map interface name in a sockaddr_dl to + * interface structure pointer. + */ +struct ifnet * +if_withname(sa) + struct sockaddr *sa; +{ + char ifname[IFNAMSIZ+1]; + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + + if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) || + (sdl->sdl_nlen > IFNAMSIZ) ) + return NULL; + + /* + * ifunit wants a null-terminated name. It may not be null-terminated + * in the sockaddr. We don't want to change the caller's sockaddr, + * and there might not be room to put the trailing null anyway, so we + * make a local copy that we know we can null terminate safely. + */ + + bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = '\0'; + return ifunit(ifname); +} + + /* * Interface ioctls. */ @@ -606,6 +672,7 @@ ifioctl(so, cmd, data, p) register struct ifreq *ifr; struct ifstat *ifs; int error; + short oif_flags; switch (cmd) { @@ -680,6 +747,9 @@ ifioctl(so, cmd, data, p) return(error); case SIOCSIFMTU: + { + u_long oldmtu = ifp->if_mtu; + error = suser(p); if (error) return (error); @@ -690,7 +760,16 @@ ifioctl(so, cmd, data, p) error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); - return(error); + /* + * If the link MTU changed, do network layer specific procedure. + */ + if (ifp->if_mtu != oldmtu) { +#ifdef INET6 + nd6_setmtu(ifp); +#endif + } + return (error); + } case SIOCADDMULTI: case SIOCDELMULTI: @@ -739,10 +818,11 @@ ifioctl(so, cmd, data, p) return ((*ifp->if_ioctl)(ifp, cmd, data)); default: + oif_flags = ifp->if_flags; if (so->so_proto == 0) return (EOPNOTSUPP); #ifndef COMPAT_43 - return ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, + error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, p)); #else @@ -793,11 +873,22 @@ ifioctl(so, cmd, data, p) case OSIOCGIFBRDADDR: case OSIOCGIFNETMASK: *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family; - } - return (error); + } } +#endif /* COMPAT_43 */ + + if ((oif_flags ^ ifp->if_flags) & IFF_UP) { +#ifdef INET6 + if (ifp->if_flags & IFF_UP) { + int s = splimp(); + in6_if_up(ifp); + splx(s); + } #endif + } + return (error); + } return (0); } @@ -960,7 +1051,7 @@ if_allmulti(ifp, onswitch) /* * Add a multicast listenership to the interface in question. - * The link layer provides a routine which converts + * The link layer provides a routine which converts */ int if_addmulti(ifp, sa, retifma) @@ -976,7 +1067,7 @@ if_addmulti(ifp, sa, retifma) * If the matching multicast address already exists * then don't add a new one, just add a reference */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma; + for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) { if (equal(sa, ifma->ifma_addr)) { ifma->ifma_refcount++; @@ -1063,7 +1154,7 @@ if_delmulti(ifp, sa) struct ifmultiaddr *ifma; int s; - for (ifma = ifp->if_multiaddrs.lh_first; ifma; + for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) if (equal(sa, ifma->ifma_addr)) break; @@ -1096,7 +1187,7 @@ if_delmulti(ifp, sa) * in the record for the link-layer address. (So we don't complain * in that case.) */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma; + for (ifma = ifp->if_multiaddrs.lh_first; ifma; ifma = ifma->ifma_link.le_next) if (equal(sa, ifma->ifma_addr)) break; diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index d6046be..dc55d54 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -56,11 +56,15 @@ #include #include -#ifdef INET +#if defined(INET) || defined(INET6) #include #include #include #endif +#ifdef INET6 +#include +#include +#endif #ifdef IPX #include @@ -112,7 +116,7 @@ extern u_char aarp_org_code[3]; #include #endif /* NVLAN > 0 */ -static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **, +static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **, struct sockaddr *)); u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define senderr(e) do { error = (e); goto bad;} while (0) @@ -146,7 +150,7 @@ static struct ng_type typestruct = { ngether_connect, ngether_rcvdata, ngether_rcvdata, - ngether_disconnect + ngether_disconnect }; #define AC2NG(AC) ((node_p)((AC)->ac_ng)) @@ -214,6 +218,17 @@ ether_output(ifp, m0, dst, rt0) type = htons(ETHERTYPE_IP); break; #endif +#ifdef INET6 + case AF_INET6: + if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) { + /* this must be impossible, so we bark */ + printf("nd6_storelladdr failed\n"); + return(0); + } + off = m->m_pkthdr.len - m->m_len; + type = htons(ETHERTYPE_IPV6); + break; +#endif #ifdef IPX case AF_IPX: type = htons(ETHERTYPE_IPX); @@ -530,6 +545,12 @@ ether_input(ifp, eh, m) inq = &ipxintrq; break; #endif +#ifdef INET6 + case ETHERTYPE_IPV6: + schednetisr(NETISR_IPV6); + inq = &ip6intrq; + break; +#endif #ifdef NS case 0x8137: /* Novell Ethernet_II Ethernet TYPE II */ schednetisr(NETISR_NS); @@ -741,6 +762,9 @@ ether_ifattach(ifp) #ifdef NETGRAPH ngether_init(ifp); #endif /* NETGRAPH */ +#ifdef INET6 + in6_ifattach_getifid(ifp); +#endif } SYSCTL_DECL(_net_link); @@ -778,7 +802,7 @@ ether_ioctl(ifp, command, data) if (ipx_nullhost(*ina)) ina->x_host = - *(union ipx_host *) + *(union ipx_host *) ac->ac_enaddr; else { bcopy((caddr_t) ina->x_host.c_host, @@ -856,11 +880,14 @@ ether_resolvemulti(ifp, llsa, sa) { struct sockaddr_dl *sdl; struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif u_char *e_addr; switch(sa->sa_family) { case AF_LINK: - /* + /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; @@ -889,9 +916,28 @@ ether_resolvemulti(ifp, llsa, sa) *llsa = (struct sockaddr *)sdl; return 0; #endif +#ifdef INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + return EADDRNOTAVAIL; + MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, + M_WAITOK); + sdl->sdl_len = sizeof *sdl; + sdl->sdl_family = AF_LINK; + sdl->sdl_index = ifp->if_index; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_nlen = 0; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + e_addr = LLADDR(sdl); + ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); + *llsa = (struct sockaddr *)sdl; + return 0; +#endif default: - /* + /* * Well, the text isn't quite right, but it's the name * that counts... */ @@ -976,8 +1022,8 @@ ngether_constructor(node_p *nodep) /* * Give our ok for a hook to be added... - * - * Allow one hook at a time (rawdata). + * + * Allow one hook at a time (rawdata). * It can eiteh rdivert everything or only unclaimed packets. */ static int @@ -1014,10 +1060,10 @@ ngether_rcvmsg(node_p node, ifp = node->private; switch (msg->header.typecookie) { - case NGM_ETHER_COOKIE: + case NGM_ETHER_COOKIE: error = EINVAL; break; - case NGM_GENERIC_COOKIE: + case NGM_GENERIC_COOKIE: switch(msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; @@ -1025,10 +1071,10 @@ ngether_rcvmsg(node_p node, int resplen = sizeof(struct ng_mesg) + 512; MALLOC(*resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT); - if (*resp == NULL) { + if (*resp == NULL) { error = ENOMEM; break; - } + } bzero(*resp, resplen); arg = (*resp)->data; @@ -1135,10 +1181,10 @@ bad: * pass an mbuf out to the connected hook * More complicated than just an m_prepend, as it tries to save later nodes * from needing to do lots of m_pullups. - */ + */ static void ngether_send(struct arpcom *ac, struct ether_header *eh, struct mbuf *m) -{ +{ int room; node_p node = AC2NG(ac); struct ether_header *eh2; @@ -1150,15 +1196,15 @@ ngether_send(struct arpcom *ac, struct ether_header *eh, struct mbuf *m) eh2 = mtod(m, struct ether_header *) - 1; if ( eh == eh2) { /* - * This is the case so just move the markers back to + * This is the case so just move the markers back to * re-include it. We lucked out. * This allows us to avoid a yucky m_pullup * in later nodes if it works. - */ - m->m_len += sizeof(*eh); + */ + m->m_len += sizeof(*eh); m->m_data -= sizeof(*eh); m->m_pkthdr.len += sizeof(*eh); - } else { + } else { /* * Alternatively there may be room even though * it is stored somewhere else. If so, copy it in. @@ -1170,7 +1216,7 @@ ngether_send(struct arpcom *ac, struct ether_header *eh, struct mbuf *m) * that fall into these cases. So we are not optimising * contorted cases. */ - + if (m->m_flags & M_EXT) { room = (mtod(m, caddr_t) - m->m_ext.ext_buf); if (room > m->m_ext.ext_size) /* garbage */ @@ -1178,14 +1224,14 @@ ngether_send(struct arpcom *ac, struct ether_header *eh, struct mbuf *m) } else { room = (mtod(m, caddr_t) - m->m_pktdat); } - if (room > sizeof (*eh)) { + if (room > sizeof (*eh)) { /* we have room, just copy it and adjust */ m->m_len += sizeof(*eh); m->m_data -= sizeof(*eh); m->m_pkthdr.len += sizeof(*eh); } else { /* - * Doing anything more is likely to get more + * Doing anything more is likely to get more * expensive than it's worth.. * it's probable that everything else is in one * big lump. The next node will do an m_pullup() @@ -1230,7 +1276,7 @@ ngether_connect(hook_p hook) /* * notify on hook disconnection (destruction) * - * For this type, removal of the last lins no effect. The interface can run + * For this type, removal of the last lins no effect. The interface can run * independently. * Since we have no per-hook information, this is rather simple. */ diff --git a/sys/net/if_gif.h b/sys/net/if_gif.h new file mode 100644 index 0000000..a402471 --- /dev/null +++ b/sys/net/if_gif.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * if_gif.h + */ + +#ifndef _NET_IF_GIF_H_ +#define _NET_IF_GIF_H_ + +#include +/* xxx sigh, why route have struct route instead of pointer? */ + +struct gif_softc { + struct ifnet gif_if; /* common area */ + struct sockaddr *gif_psrc; /* Physical src addr */ + struct sockaddr *gif_pdst; /* Physical dst addr */ + union { + struct route gifscr_ro; /* xxx */ + struct route_in6 gifscr_ro6; /* xxx */ + } gifsc_gifscr; + int gif_flags; +}; + +#define gif_ro gifsc_gifscr.gifscr_ro +#define gif_ro6 gifsc_gifscr.gifscr_ro6 + +#define GIFF_INUSE 0x1 /* gif is in use */ + +#define GIF_MTU (1280) /* Default MTU */ +#define GIF_MTU_MIN (1280) /* Minimum MTU */ +#define GIF_MTU_MAX (8192) /* Maximum MTU */ + +extern int ngif; +extern struct gif_softc *gif; + +/* Prototypes */ +void gif_input __P((struct mbuf *, int, struct ifnet *)); +int gif_output __P((struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *)); +int gif_ioctl __P((struct ifnet *, u_long, caddr_t)); + +#endif /* _NET_IF_GIF_H_ */ diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c index de692e5..c57f857 100644 --- a/sys/net/if_loop.c +++ b/sys/net/if_loop.c @@ -67,6 +67,14 @@ #include #endif +#ifdef INET6 +#ifndef INET +#include +#endif +#include +#include +#endif + #ifdef NS #include #include @@ -93,6 +101,8 @@ static int looutput __P((struct ifnet *ifp, #ifdef TINY_LOMTU #define LOMTU (1024+512) +#elif defined(LARGE_LOMTU) +#define LOMTU 131072 #else #define LOMTU 16384 #endif @@ -136,11 +146,41 @@ looutput(ifp, m, dst, rt) return (rt->rt_flags & RTF_BLACKHOLE ? 0 : rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); } + /* + * KAME requires that the packet to be contiguous on the + * mbuf. We need to make that sure. + * this kind of code should be avoided. + * XXX: fails to join if interface MTU > MCLBYTES. jumbogram? + */ + if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) { + struct mbuf *n; + + MGETHDR(n, M_DONTWAIT, MT_HEADER); + if (!n) + goto contiguousfail; + MCLGET(n, M_DONTWAIT); + if (! (n->m_flags & M_EXT)) { + m_freem(n); + goto contiguousfail; + } + + m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t)); + n->m_pkthdr = m->m_pkthdr; + n->m_len = m->m_pkthdr.len; + m_freem(m); + m = n; + } + if (0) { +contiguousfail: + printf("looutput: mbuf allocation failed\n"); + } + ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; #if 1 /* XXX */ switch (dst->sa_family) { case AF_INET: + case AF_INET6: case AF_IPX: case AF_NS: case AF_ISO: @@ -227,6 +267,13 @@ if_simloop(ifp, m, dst, hlen) isr = NETISR_IP; break; #endif +#ifdef INET6 + case AF_INET6: + m->m_flags |= M_LOOP; + ifq = &ip6intrq; + isr = NETISR_IPV6; + break; +#endif #ifdef IPX case AF_IPX: ifq = &ipxintrq; @@ -285,7 +332,7 @@ lortrequest(cmd, rt, sa) * should be at least twice the MTU plus a little more for * overhead. */ - rt->rt_rmx.rmx_recvpipe = + rt->rt_rmx.rmx_recvpipe = rt->rt_rmx.rmx_sendpipe = 3 * LOMTU; } } @@ -327,6 +374,10 @@ loioctl(ifp, cmd, data) case AF_INET: break; #endif +#ifdef INET6 + case AF_INET6: + break; +#endif default: error = EAFNOSUPPORT; diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 0b6e40d..8d497d4 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -166,6 +166,10 @@ typedef void if_init_f_t __P((void *)); #define if_xmitquota if_data.ifi_xmitquota #define if_rawoutput(if, m, sa) if_output(if, m, sa, (struct rtentry *)0) +/* for compatibility with other BSDs */ +#define if_addrlist if_addrhead +#define if_list if_link + /* * Bit values in if_ipending */ @@ -270,16 +274,19 @@ struct ifaddr { }; #define IFA_ROUTE RTF_UP /* route installed */ +/* for compatibility with other BSDs */ +#define ifa_list ifa_link + /* * The prefix structure contains information about one prefix * of an interface. They are maintained by the different address families, * are allocated and attached when an prefix or an address is set, - * and are linked together so all prfefixes for an interface can be located. + * and are linked together so all prefixes for an interface can be located. */ struct ifprefix { struct sockaddr *ifpr_prefix; /* prefix of interface */ struct ifnet *ifpr_ifp; /* back-pointer to interface */ - TAILQ_ENTRY(ifprefix) *ifpr_list; /* queue macro glue */ + TAILQ_ENTRY(ifprefix) ifpr_list; /* queue macro glue */ u_char ifpr_plen; /* prefix length in bits */ u_char ifpr_type; /* protocol dependent prefix type */ }; @@ -321,7 +328,7 @@ int ether_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); int ether_ioctl __P((struct ifnet *, int, caddr_t)); -int if_addmulti __P((struct ifnet *, struct sockaddr *, +int if_addmulti __P((struct ifnet *, struct sockaddr *, struct ifmultiaddr **)); int if_allmulti __P((struct ifnet *, int)); void if_attach __P((struct ifnet *)); @@ -352,7 +359,7 @@ struct ifaddr *ifa_ifwithroute __P((int, struct sockaddr *, struct ifaddr *ifaof_ifpforaddr __P((struct sockaddr *, struct ifnet *)); void ifafree __P((struct ifaddr *)); -struct ifmultiaddr *ifmaof_ifpforaddr __P((struct sockaddr *, +struct ifmultiaddr *ifmaof_ifpforaddr __P((struct sockaddr *, struct ifnet *)); int if_simloop __P((struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, int hlen)); diff --git a/sys/net/net_osdep.c b/sys/net/net_osdep.c new file mode 100644 index 0000000..81dd3a8 --- /dev/null +++ b/sys/net/net_osdep.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +const char * +if_name(ifp) + struct ifnet *ifp; +{ + static char nam[IFNAMSIZ + 10]; /*enough?*/ + + snprintf(nam, sizeof(nam), "%s%d", ifp->if_name, ifp->if_unit); + return nam; +} diff --git a/sys/net/net_osdep.h b/sys/net/net_osdep.h new file mode 100644 index 0000000..11fc27c --- /dev/null +++ b/sys/net/net_osdep.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * glue for kernel code programming differences. + */ + +/* + * OS dependencies: + * + * - privileged process + * NetBSD, FreeBSD 3 + * struct proc *p; + * if (p && !suser(p->p_ucred, &p->p_acflag)) + * privileged; + * OpenBSD, BSDI [34], FreeBSD 2 + * struct socket *so; + * if (so->so_state & SS_PRIV) + * privileged; + * - foo_control + * NetBSD, FreeBSD 3 + * needs to give struct proc * as argument + * OpenBSD, BSDI [34], FreeBSD 2 + * do not need struct proc * + * - bpf: + * OpenBSD, NetBSD, BSDI [34] + * need caddr_t * (= if_bpf **) and struct ifnet * + * FreeBSD 2, FreeBSD 3 + * need only struct ifnet * as argument + * - struct ifnet + * use queue.h? member names if name + * --- --- --- + * FreeBSD 2 no old standard if_name+unit + * FreeBSD 3 yes strange if_name+unit + * OpenBSD yes standard if_xname + * NetBSD yes standard if_xname + * BSDI [34] no old standard if_name+unit + * - usrreq + * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 + * single function with PRU_xx, arguments are mbuf + * FreeBSD 3 + * separates functions, non-mbuf arguments + * - {set,get}sockopt + * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 + * manipulation based on mbuf + * FreeBSD 3 + * non-mbuf manipulation using sooptcopy{in,out}() + * - timeout() and untimeout() + * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 + * timeout() is a void function + * FreeBSD 3 + * timeout() is non-void, must keep returned value for untimeuot() + * - sysctl + * NetBSD, OpenBSD + * foo_sysctl() + * BSDI [34] + * foo_sysctl() but with different style + * FreeBSD 2, FreeBSD 3 + * linker hack + * + * - if_ioctl + * NetBSD, FreeBSD 3, BSDI [34] + * 2nd argument is u_long cmd + * FreeBSD 2 + * 2nd argument is int cmd + * - if attach routines + * NetBSD + * void xxattach(int); + * FreeBSD 2, FreeBSD 3 + * void xxattach(void *); + * PSEUDO_SET(xxattach, if_xx); + * + * - ovbcopy() + * in NetBSD 1.4 or later, ovbcopy() is not supplied in the kernel. + * bcopy() is safe against overwrites. + * - splnet() + * NetBSD 1.4 or later requires splsoftnet(). + * other operating systems use splnet(). + * + * - dtom() + * NEVER USE IT! + */ + +#ifndef __NET_NET_OSDEP_H_DEFINED_ +#define __NET_NET_OSDEP_H_DEFINED_ +#ifdef _KERNEL + +struct ifnet; +extern const char *if_name __P((struct ifnet *)); + +#define HAVE_OLD_BPF + +#endif /*_KERNEL*/ +#endif /*__NET_NET_OSDEP_H_DEFINED_ */ diff --git a/sys/net/pfkeyv2.h b/sys/net/pfkeyv2.h new file mode 100644 index 0000000..ac509e0 --- /dev/null +++ b/sys/net/pfkeyv2.h @@ -0,0 +1,420 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* $Id: keyv2.h,v 1.1.6.1.6.4 1999/06/08 05:33:39 itojun Exp $ */ + +/* + * This file has been derived rfc 2367, + * And added some flags of SADB_KEY_FLAGS_ as SADB_X_EXT_. + * sakane@ydc.co.jp + */ + +#ifndef _NET_PFKEYV2_H_ +#define _NET_PFKEYV2_H_ + +/* +This file defines structures and symbols for the PF_KEY Version 2 +key management interface. It was written at the U.S. Naval Research +Laboratory. This file is in the public domain. The authors ask that +you leave this credit intact on any copies of this file. +*/ +#ifndef __PFKEY_V2_H +#define __PFKEY_V2_H 1 + +#define PF_KEY_V2 2 +#define PFKEYV2_REVISION 199806L + +#define SADB_RESERVED 0 +#define SADB_GETSPI 1 +#define SADB_UPDATE 2 +#define SADB_ADD 3 +#define SADB_DELETE 4 +#define SADB_GET 5 +#define SADB_ACQUIRE 6 +#define SADB_REGISTER 7 +#define SADB_EXPIRE 8 +#define SADB_FLUSH 9 +#define SADB_DUMP 10 +#define SADB_X_PROMISC 11 +#define SADB_X_PCHANGE 12 + +#define SADB_X_SPDUPDATE 13 /* not yet */ +#define SADB_X_SPDADD 14 +#define SADB_X_SPDDELETE 15 +#define SADB_X_SPDGET 16 /* not yet */ +#define SADB_X_SPDACQUIRE 17 /* not yet */ +#define SADB_X_SPDDUMP 18 +#define SADB_X_SPDFLUSH 19 +#define SADB_MAX 19 + +struct sadb_msg { + u_int8_t sadb_msg_version; + u_int8_t sadb_msg_type; + u_int8_t sadb_msg_errno; + u_int8_t sadb_msg_satype; + u_int16_t sadb_msg_len; + u_int8_t sadb_msg_mode; /* XXX */ + u_int8_t sadb_msg_reserved; + u_int32_t sadb_msg_seq; + u_int32_t sadb_msg_pid; +}; + +struct sadb_ext { + u_int16_t sadb_ext_len; + u_int16_t sadb_ext_type; +}; + +struct sadb_sa { + u_int16_t sadb_sa_len; + u_int16_t sadb_sa_exttype; + u_int32_t sadb_sa_spi; + u_int8_t sadb_sa_replay; + u_int8_t sadb_sa_state; + u_int8_t sadb_sa_auth; + u_int8_t sadb_sa_encrypt; + u_int32_t sadb_sa_flags; +}; + +struct sadb_lifetime { + u_int16_t sadb_lifetime_len; + u_int16_t sadb_lifetime_exttype; + u_int32_t sadb_lifetime_allocations; + u_int64_t sadb_lifetime_bytes; + u_int64_t sadb_lifetime_addtime; + u_int64_t sadb_lifetime_usetime; +}; + +struct sadb_address { + u_int16_t sadb_address_len; + u_int16_t sadb_address_exttype; + u_int8_t sadb_address_proto; + u_int8_t sadb_address_prefixlen; + u_int16_t sadb_address_reserved; +}; + +struct sadb_key { + u_int16_t sadb_key_len; + u_int16_t sadb_key_exttype; + u_int16_t sadb_key_bits; + u_int16_t sadb_key_reserved; +}; + +struct sadb_ident { + u_int16_t sadb_ident_len; + u_int16_t sadb_ident_exttype; + u_int16_t sadb_ident_type; + u_int16_t sadb_ident_reserved; + u_int64_t sadb_ident_id; +}; +/* in order to use to divide sadb_ident.sadb_ident_id */ +union sadb_x_ident_id { + u_int64_t sadb_x_ident_id; + struct _sadb_x_ident_id_addr { + u_int16_t prefix; + u_int16_t ul_proto; + u_int32_t reserved; + } sadb_x_ident_id_addr; +}; + +struct sadb_sens { + u_int16_t sadb_sens_len; + u_int16_t sadb_sens_exttype; + u_int32_t sadb_sens_dpd; + u_int8_t sadb_sens_sens_level; + u_int8_t sadb_sens_sens_len; + u_int8_t sadb_sens_integ_level; + u_int8_t sadb_sens_integ_len; + u_int32_t sadb_sens_reserved; +}; + +struct sadb_prop { + u_int16_t sadb_prop_len; + u_int16_t sadb_prop_exttype; + u_int8_t sadb_prop_replay; + u_int8_t sadb_prop_reserved[3]; +}; + +struct sadb_comb { + u_int8_t sadb_comb_auth; + u_int8_t sadb_comb_encrypt; + u_int16_t sadb_comb_flags; + u_int16_t sadb_comb_auth_minbits; + u_int16_t sadb_comb_auth_maxbits; + u_int16_t sadb_comb_encrypt_minbits; + u_int16_t sadb_comb_encrypt_maxbits; + u_int32_t sadb_comb_reserved; + u_int32_t sadb_comb_soft_allocations; + u_int32_t sadb_comb_hard_allocations; + u_int64_t sadb_comb_soft_bytes; + u_int64_t sadb_comb_hard_bytes; + u_int64_t sadb_comb_soft_addtime; + u_int64_t sadb_comb_hard_addtime; + u_int64_t sadb_comb_soft_usetime; + u_int64_t sadb_comb_hard_usetime; +}; + +struct sadb_supported { + u_int16_t sadb_supported_len; + u_int16_t sadb_supported_exttype; + u_int32_t sadb_supported_reserved; +}; + +struct sadb_alg { + u_int8_t sadb_alg_id; + u_int8_t sadb_alg_ivlen; + u_int16_t sadb_alg_minbits; + u_int16_t sadb_alg_maxbits; + u_int16_t sadb_alg_reserved; +}; + +struct sadb_spirange { + u_int16_t sadb_spirange_len; + u_int16_t sadb_spirange_exttype; + u_int32_t sadb_spirange_min; + u_int32_t sadb_spirange_max; + u_int32_t sadb_spirange_reserved; +}; + +struct sadb_x_kmprivate { + u_int16_t sadb_x_kmprivate_len; + u_int16_t sadb_x_kmprivate_exttype; + u_int32_t sadb_x_kmprivate_reserved; +}; + +/* XXX Policy Extension */ +/* sizeof(struct sadb_x_policy) == 8 */ +struct sadb_x_policy { + u_int16_t sadb_x_policy_len; + u_int16_t sadb_x_policy_exttype; + /* See policy type of ipsec.h */ + u_int16_t sadb_x_policy_type; + u_int8_t sadb_x_policy_dir; /* direction, see ipsec.h */ + u_int8_t sadb_x_policy_reserved; +}; +/* + * When policy_type == IPSEC, it is followed by some of + * the ipsec policy request. + * [total length of ipsec policy requests] + * = (sadb_x_policy_len * sizeof(uint64_t) - sizeof(struct sadb_x_policy)) + */ + +/* XXX IPsec Policy Request Extension */ +/* + * This structure is aligned 8 bytes. + */ +struct sadb_x_ipsecrequest { + u_int16_t sadb_x_ipsecrequest_len; + /* structure length aligned to 8 bytes. + * This value is true length of bytes. + * Not in units of 64 bits. */ + u_int16_t sadb_x_ipsecrequest_proto; /* See ipsec.h */ + /* See ipsec.h. Not SADB_SATYPE_XX */ + u_int16_t sadb_x_ipsecrequest_mode; + u_int16_t sadb_x_ipsecrequest_level; /* See ipsec.h */ + + /* + * followed by source IP address of SA, and immediately followed by + * destination IP address of SA. These encoded into two of sockaddr + * structure without any padding. Must set each sa_len exactly. + * Each of length of the sockaddr structure are not aligned to 64bits, + * but sum of x_request and addresses is aligned to 64bits. + */ +}; + +#define SADB_EXT_RESERVED 0 +#define SADB_EXT_SA 1 +#define SADB_EXT_LIFETIME_CURRENT 2 +#define SADB_EXT_LIFETIME_HARD 3 +#define SADB_EXT_LIFETIME_SOFT 4 +#define SADB_EXT_ADDRESS_SRC 5 +#define SADB_EXT_ADDRESS_DST 6 +#define SADB_EXT_ADDRESS_PROXY 7 +#define SADB_EXT_KEY_AUTH 8 +#define SADB_EXT_KEY_ENCRYPT 9 +#define SADB_EXT_IDENTITY_SRC 10 +#define SADB_EXT_IDENTITY_DST 11 +#define SADB_EXT_SENSITIVITY 12 +#define SADB_EXT_PROPOSAL 13 +#define SADB_EXT_SUPPORTED_AUTH 14 +#define SADB_EXT_SUPPORTED_ENCRYPT 15 +#define SADB_EXT_SPIRANGE 16 +#define SADB_X_EXT_KMPRIVATE 17 +#define SADB_X_EXT_POLICY 18 +#define SADB_EXT_MAX 18 + +#define SADB_SATYPE_UNSPEC 0 +#define SADB_SATYPE_AH 2 +#define SADB_SATYPE_ESP 3 +#define SADB_SATYPE_RSVP 5 +#define SADB_SATYPE_OSPFV2 6 +#define SADB_SATYPE_RIPV2 7 +#define SADB_SATYPE_MIP 8 +#define SADB_X_SATYPE_IPCOMP 9 +#define SADB_SATYPE_MAX 9 + +#define SADB_SASTATE_LARVAL 0 +#define SADB_SASTATE_MATURE 1 +#define SADB_SASTATE_DYING 2 +#define SADB_SASTATE_DEAD 3 +#define SADB_SASTATE_MAX 3 +#define SADB_SAFLAGS_PFS 1 + +#define SADB_AALG_NONE 0 +#define SADB_AALG_MD5HMAC 1 /* 2 */ +#define SADB_AALG_SHA1HMAC 2 /* 3 */ +#define SADB_AALG_MD5 3 /* Keyed MD5 */ +#define SADB_AALG_SHA 4 /* Keyed SHA */ +#define SADB_AALG_NULL 5 /* null authentication */ +#define SADB_AALG_MAX 6 + +#define SADB_EALG_NONE 0 +#define SADB_EALG_DESCBC 1 /* 2 */ +#define SADB_EALG_3DESCBC 2 /* 3 */ +#define SADB_EALG_NULL 3 /* 11 */ +#define SADB_EALG_BLOWFISHCBC 4 +#define SADB_EALG_CAST128CBC 5 +#define SADB_EALG_RC5CBC 6 +#define SADB_EALG_MAX 7 + +/*nonstandard */ +#define SADB_X_CALG_NONE 0 +#define SADB_X_CALG_OUI 1 +#define SADB_X_CALG_DEFLATE 2 +#define SADB_X_CALG_LZS 3 + +#define SADB_IDENTTYPE_RESERVED 0 +#define SADB_IDENTTYPE_PREFIX 1 +#define SADB_IDENTTYPE_FQDN 2 +#define SADB_IDENTTYPE_USERFQDN 3 +#define SADB_X_IDENTTYPE_ADDR 4 +#define SADB_IDENTTYPE_MAX 4 + +/* `flags' in sadb_sa structure holds followings */ +#define SADB_X_EXT_NONE 0x0000 /* i.e. new format. */ +#define SADB_X_EXT_OLD 0x0001 /* old format. */ + +#define SADB_X_EXT_IV4B 0x0010 /* IV length of 4 bytes in use */ +#define SADB_X_EXT_DERIV 0x0020 /* DES derived */ +#define SADB_X_EXT_CYCSEQ 0x0040 /* allowing to cyclic sequence. */ + + /* three of followings are exclusive flags each them */ +#define SADB_X_EXT_PSEQ 0x0000 /* sequencial padding for ESP */ +#define SADB_X_EXT_PRAND 0x0100 /* random padding for ESP */ +#define SADB_X_EXT_PZERO 0x0200 /* zero padding for ESP */ +#define SADB_X_EXT_PMASK 0x0300 /* mask for padding flag */ + +#define SADB_X_EXT_RAWCPI 0x0080 /* use well known CPI (IPComp) */ + +#define SADB_KEY_FLAGS_MAX 0x0fff + +/* SPI size for PF_KEYv2 */ +#define PFKEY_SPI_SIZE sizeof(u_int32_t) + +/* Identifier for menber of lifetime structure */ +#define SADB_X_LIFETIME_ALLOCATIONS 0 +#define SADB_X_LIFETIME_BYTES 1 +#define SADB_X_LIFETIME_ADDTIME 2 +#define SADB_X_LIFETIME_USETIME 3 + +/* The rate for SOFT lifetime against HARD one. */ +#define PFKEY_SOFT_LIFETIME_RATE 80 + +/* Utilities */ +#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1))) +#define PFKEY_EXTLEN(msg) \ + PFKEY_UNUNIT64(((struct sadb_ext *)(msg))->sadb_ext_len) +#define PFKEY_ADDR_PREFIX(ext) \ + (((struct sadb_address *)(ext))->sadb_address_prefixlen) +#define PFKEY_ADDR_PROTO(ext) \ + (((struct sadb_address *)(ext))->sadb_address_proto) +#define PFKEY_ADDR_SADDR(ext) \ + ((struct sockaddr *)((caddr_t)(ext) + sizeof(struct sadb_address))) + +/* in 64bits */ +#define PFKEY_UNUNIT64(a) ((a) << 3) +#define PFKEY_UNIT64(a) ((a) >> 3) + +#ifndef KERNEL +extern void pfkey_sadump(struct sadb_msg *m); +extern void pfkey_spdump(struct sadb_msg *m); + +struct sockaddr; +int ipsec_check_keylen __P((u_int supported, u_int alg_id, u_int keylen)); +u_int pfkey_set_softrate __P((u_int type, u_int rate)); +u_int pfkey_get_softrate __P((u_int type)); +int pfkey_send_getspi __P((int so, u_int satype, u_int mode, + struct sockaddr *src, struct sockaddr *dst, + u_int32_t min, u_int32_t max, u_int32_t seq)); +int pfkey_send_update __P((int so, u_int satype, u_int mode, + struct sockaddr *src, struct sockaddr *dst, + u_int32_t spi, u_int wsize, caddr_t keymat, + u_int e_type, u_int e_keylen, u_int a_type, + u_int a_keylen, u_int flags, u_int32_t l_alloc, + u_int64_t l_bytes, u_int64_t l_addtime, + u_int64_t l_usetime, u_int32_t seq)); +int pfkey_send_add __P((int so, u_int satype, u_int mode, + struct sockaddr *src, struct sockaddr *dst, + u_int32_t spi, u_int wsize, caddr_t keymat, + u_int e_type, u_int e_keylen, u_int a_type, + u_int a_keylen, u_int flags, u_int32_t l_alloc, + u_int64_t l_bytes, u_int64_t l_addtime, + u_int64_t l_usetime, u_int32_t seq)); +int pfkey_send_delete __P((int so, u_int satype, u_int mode, + struct sockaddr *src, struct sockaddr *dst, + u_int32_t spi)); +int pfkey_send_get __P((int so, u_int satype, u_int mode, + struct sockaddr *src, struct sockaddr *dst, + u_int32_t spi)); +int pfkey_send_register __P((int so, u_int satype)); +int pfkey_recv_register __P((int so)); +int pfkey_send_flush __P((int so, u_int satype)); +int pfkey_send_dump __P((int so, u_int satype)); +int pfkey_send_promisc_toggle __P((int so, int flag)); +int pfkey_send_spdadd __P((int so, struct sockaddr *src, u_int prefs, + struct sockaddr *dst, u_int prefd, u_int proto, + caddr_t policy, int policylen, u_int32_t seq)); +int pfkey_send_spddelete __P((int so, struct sockaddr *src, u_int prefs, + struct sockaddr *dst, u_int prefd, u_int proto, u_int32_t seq)); +int pfkey_send_spdflush __P((int so)); +int pfkey_send_spddump __P((int so)); + +int pfkey_open __P((void)); +void pfkey_close __P((int so)); +struct sadb_msg *pfkey_recv __P((int so)); +int pfkey_send __P((int so, struct sadb_msg *msg, int len)); +int pfkey_align __P((struct sadb_msg *msg, caddr_t *mhp)); +int pfkey_check __P((caddr_t *mhp)); + +#endif /*!KERNEL*/ + +#endif /* __PFKEY_V2_H */ + +#endif /* _NET_PFKEYV2_H_ */ diff --git a/sys/net/route.c b/sys/net/route.c index eddd18a..782c471 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -103,6 +103,16 @@ rtalloc_ign(ro, ignore) ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); } +/* for INET6 */ +void +rtcalloc(ro) + register struct route *ro; +{ + if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) + return; /* XXX */ + ro->ro_rt = rtalloc1(&ro->ro_dst, RTF_CLONING, 0UL); +} + /* * Look up the route that matches the address given * Or, at least try.. Create a cloned route if needed. @@ -121,7 +131,7 @@ rtalloc1(dst, report, ignflags) u_long nflags; int s = splnet(), err = 0, msgtype = RTM_MISS; - /* + /* * Look up the address in the table for that Address Family */ if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && @@ -151,7 +161,7 @@ rtalloc1(dst, report, ignflags) } if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { /* - * If the new route specifies it be + * If the new route specifies it be * externally resolved, then go do that. */ msgtype = RTM_RESOLVE; @@ -216,7 +226,7 @@ rtfree(rt) if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtfree 2"); - /* + /* * the rtentry must have been removed from the routing table * so it is represented in rttrash.. remove that now. */ @@ -229,7 +239,7 @@ rtfree(rt) } #endif - /* + /* * release references on items we hold them on.. * e.g other routes and ifaddrs. */ @@ -513,7 +523,7 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) */ rt->rt_flags &= ~RTF_UP; - /* + /* * give the protocol a chance to keep things in sync. */ if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) @@ -593,6 +603,7 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) ifa->ifa_refcnt++; rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; + /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, rnh, rt->rt_nodes); @@ -607,7 +618,7 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) */ rt2 = rtalloc1(dst, 0, RTF_PRCLONING); if (rt2 && rt2->rt_parent) { - rtrequest(RTM_DELETE, + rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt2), rt2->rt_gateway, rt_mask(rt2), rt2->rt_flags, 0); @@ -638,9 +649,9 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) rt->rt_parent = 0; - /* + /* * If we got here from RESOLVE, then we are cloning - * so clone the rest, and note that we + * so clone the rest, and note that we * are a clone (and increment the parent's references) */ if (req == RTM_RESOLVE) { @@ -846,8 +857,8 @@ rt_setgate(rt0, dst, gate) */ Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); - /* - * if we are replacing the chunk (or it's new) we need to + /* + * if we are replacing the chunk (or it's new) we need to * replace the dst as well */ if (old) { @@ -941,13 +952,15 @@ rtinit(ifa, cmd, flags) * be confusing at best and possibly worse. */ if (cmd == RTM_DELETE) { - /* + /* * It's a delete, so it should already exist.. * If it's a net, mask off the host bits * (Assuming we have a mask) */ if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { - m = m_get(M_WAIT, MT_SONAME); + m = m_get(M_DONTWAIT, MT_SONAME); + if (m == NULL) + return(ENOBUFS); deldst = mtod(m, struct sockaddr *); rt_maskedcopy(dst, deldst, ifa->ifa_netmask); dst = deldst; @@ -971,7 +984,7 @@ rtinit(ifa, cmd, flags) * If the interface in the rtentry doesn't match * the interface we are using, then we don't * want to delete it, so return an error. - * This seems to be the only point of + * This seems to be the only point of * this whole RTM_DELETE clause. */ if (m) @@ -983,7 +996,7 @@ rtinit(ifa, cmd, flags) /* XXX */ #if 0 else { - /* + /* * One would think that as we are deleting, and we know * it doesn't exist, we could just return at this point * with an "ELSE" clause, but apparently not.. @@ -1025,7 +1038,7 @@ rtinit(ifa, cmd, flags) */ rt->rt_refcnt--; /* - * If it came back with an unexpected interface, then it must + * If it came back with an unexpected interface, then it must * have already existed or something. (XXX) */ if (rt->rt_ifa != ifa) { @@ -1038,7 +1051,7 @@ rtinit(ifa, cmd, flags) */ if (rt->rt_ifa->ifa_rtrequest) rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); - /* + /* * Remove the referenve to the it's ifaddr. */ IFAFREE(rt->rt_ifa); @@ -1048,6 +1061,7 @@ rtinit(ifa, cmd, flags) */ rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; + rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu; /*XXX*/ ifa->ifa_refcnt++; /* * Now ask the protocol to check if it needs diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h new file mode 100644 index 0000000..6d418d6 --- /dev/null +++ b/sys/netinet/icmp6.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETINET_ICMP6_H_ +#define _NETINET_ICMP6_H_ + +#include + +#endif /* !_NETINET_ICMP6_H_ */ diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index fdba717..d19d449 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -66,12 +66,12 @@ static void in_rtchange __P((struct inpcb *, int)); * These configure the range of local port addresses assigned to * "unspecified" outgoing connections/packets/whatever. */ -static int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ -static int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ -static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ -static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ -static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 49152 */ -static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 65535 */ +int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ +int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ +int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ +int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ +int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 49152 */ +int ipport_hilastauto = IPPORT_HILASTAUTO; /* 65535 */ #define RANGECHK(var, min, max) \ if ((var) < (min)) { (var) = (min); } \ @@ -204,7 +204,7 @@ in_pcbbind(inp, nam, p) if (so->so_cred->cr_uid != 0 && !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { t = in_pcblookup_local(inp->inp_pcbinfo, - sin->sin_addr, lport, + sin->sin_addr, lport, prison ? 0 : INPLOOKUP_WILDCARD); if (t && (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || @@ -409,7 +409,7 @@ in_pcbladdr(inp, nam, plocal_sin) imo = inp->inp_moptions; if (imo->imo_multicast_ifp != NULL) { ifp = imo->imo_multicast_ifp; - for (ia = in_ifaddrhead.tqh_first; ia; + for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) if (ia->ia_ifp == ifp) break; diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index c891207..adfa5fa 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -39,6 +39,12 @@ #include + +#include /* for IPSEC */ + +#define in6pcb inpcb /* for KAME src sync over BSD*'s */ +#define in6p_sp inp_sp /* for KAME src sync over BSD*'s */ + /* * Common structure pcb for internet protocol implementation. * Here are stored pointers to local and foreign host table @@ -51,32 +57,111 @@ LIST_HEAD(inpcbporthead, inpcbport); typedef u_quad_t inp_gen_t; /* + * PCB with AF_INET6 null bind'ed laddr can receive AF_INET input packet. + * So, AF_INET6 null laddr is also used as AF_INET null laddr, + * by utilize following structure. (At last, same as INRIA) + */ +struct in_addr_4in6 { + u_int32_t ia46_pad32[3]; + struct in_addr ia46_addr4; +}; + +/* * NB: the zone allocator is type-stable EXCEPT FOR THE FIRST TWO LONGS * of the structure. Therefore, it is important that the members in * that position not contain any information which is required to be * stable. */ +struct icmp6_filter; + struct inpcb { - LIST_ENTRY(inpcb) inp_hash; /* hash list */ - struct in_addr inp_faddr; /* foreign host table entry */ - struct in_addr inp_laddr; /* local host table entry */ + LIST_ENTRY(inpcb) inp_hash; /* hash list */ u_short inp_fport; /* foreign port */ u_short inp_lport; /* local port */ - LIST_ENTRY(inpcb) inp_list; /* list for all PCBs of this proto */ + LIST_ENTRY(inpcb) inp_list; /* list for all PCBs of this proto */ + u_int32_t inp_flow; + + /* protocol dependent part, local and foreign addr */ + union { + /* foreign host table entry */ + struct in_addr_4in6 inp46_foreign; + struct in6_addr inp6_foreign; + } inp_dependfaddr; + union { + /* local host table entry */ + struct in_addr_4in6 inp46_local; + struct in6_addr inp6_local; + } inp_dependladdr; + caddr_t inp_ppcb; /* pointer to per-protocol pcb */ struct inpcbinfo *inp_pcbinfo; /* PCB list info */ struct socket *inp_socket; /* back pointer to socket */ - struct mbuf *inp_options; /* IP options */ - struct route inp_route; /* placeholder for routing entry */ + /* list for this PCB's local port */ int inp_flags; /* generic IP/datagram flags */ - u_char inp_ip_tos; /* type of service proto */ + + /* protocol dependent part; cached route */ + union { + /* placeholder for routing entry */ + struct route inp4_route; + struct route_in6 inp6_route; + } inp_dependroute; + + struct inpcbpolicy *inp_sp; /* for IPSEC */ + u_char inp_vflag; +#define INP_IPV4 0x1 +#define INP_IPV6 0x2 u_char inp_ip_ttl; /* time to live proto */ u_char inp_ip_p; /* protocol proto */ - u_char pad[1]; /* alignment */ - struct ip_moptions *inp_moptions; /* IP multicast options */ - LIST_ENTRY(inpcb) inp_portlist; /* list for this PCB's local port */ + + /* protocol dependent part; options */ + struct { + u_char inp4_ip_tos; /* type of service proto */ + struct mbuf *inp4_options; /* IP options */ + struct ip_moptions *inp4_moptions; /* IP multicast options */ + } inp_depend4; +#define inp_faddr inp_dependfaddr.inp46_foreign.ia46_addr4 +#define inp_laddr inp_dependladdr.inp46_local.ia46_addr4 +#define inp_route inp_dependroute.inp4_route +#define inp_ip_tos inp_depend4.inp4_ip_tos +#define inp_options inp_depend4.inp4_options +#define inp_moptions inp_depend4.inp4_moptions + struct { + /* IP options */ + struct mbuf *inp6_options; + /* IP6 options for outgoing packets */ + struct ip6_pktopts *inp6_outputopts; + /* IP multicast options */ + struct ip6_moptions *inp6_moptions; + /* ICMPv6 code type filter */ + struct icmp6_filter *inp6_icmp6filt; + /* IPV6_CHECKSUM setsockopt */ + int inp6_cksum; + u_short inp6_ifindex; + short inp6_hops; + u_int8_t inp6_hlim; + } inp_depend6; + LIST_ENTRY(inpcb) inp_portlist; struct inpcbport *inp_phd; /* head of this list */ - inp_gen_t inp_gencnt; /* generation count of this instance */ + inp_gen_t inp_gencnt; /* generation count of this instance */ +#define in6p_faddr inp_dependfaddr.inp6_foreign +#define in6p_laddr inp_dependladdr.inp6_local +#define in6p_route inp_dependroute.inp6_route +#define in6p_ip6_hlim inp_depend6.inp6_hlim +#define in6p_hops inp_depend6.inp6_hops /* default hop limit */ +#define in6p_ip6_nxt inp_ip_p +#define in6p_flowinfo inp_flow +#define in6p_vflag inp_vflag +#define in6p_options inp_depend6.inp6_options +#define in6p_outputopts inp_depend6.inp6_outputopts +#define in6p_moptions inp_depend6.inp6_moptions +#define in6p_icmp6filt inp_depend6.inp6_icmp6filt +#define in6p_cksum inp_depend6.inp6_cksum +#define inp6_ifindex inp_depend6.inp6_ifindex +#define in6p_flags inp_flags /* for KAME src sync over BSD*'s */ +#define in6p_socket inp_socket /* for KAME src sync over BSD*'s */ +#define in6p_lport inp_lport /* for KAME src sync over BSD*'s */ +#define in6p_fport inp_fport /* for KAME src sync over BSD*'s */ +#define in6p_ppcb inp_ppcb /* for KAME src sync over BSD*'s */ }; /* * The range of the generation count, as used in this implementation, @@ -140,14 +225,54 @@ struct inpcbinfo { /* XXX documentation, prefixes */ #define INP_ANONPORT 0x40 /* port chosen for user */ #define INP_RECVIF 0x80 /* receive incoming interface */ #define INP_MTUDISC 0x100 /* user can do MTU discovery */ +#define INP_FAITH 0x200 /* accept FAITH'ed connections */ +#define IN6P_PKTINFO 0x010000 +#define IN6P_HOPLIMIT 0x020000 +#define IN6P_NEXTHOP 0x040000 +#define IN6P_HOPOPTS 0x080000 +#define IN6P_DSTOPTS 0x100000 +#define IN6P_RTHDR 0x200000 +#define IN6P_BINDV6ONLY 0x400000 #define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\ - INP_RECVIF) + INP_RECVIF|\ + IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_NEXTHOP|\ + IN6P_HOPOPTS|IN6P_DSTOPTS|IN6P_RTHDR) -#define INPLOOKUP_WILDCARD 1 +#define INP_UNMAPPABLEOPTS (IN6P_HOPOPTS|IN6P_DSTOPTS|IN6P_RTHDR) + + /* for KAME src sync over BSD*'s */ +#define IN6P_RECVOPTS INP_RECVOPTS +#define IN6P_RECVRETOPTS INP_RECVRETOPTS +#define IN6P_RECVDSTADDR INP_RECVDSTADDR +#define IN6P_HDRINCL INP_HDRINCL +#define IN6P_HIGHPORT INP_HIGHPORT +#define IN6P_LOWPORT INP_LOWPORT +#define IN6P_ANONPORT INP_ANONPORT +#define IN6P_RECVIF INP_RECVIF +#define IN6P_MTUDISC INP_MTUDISC +#define IN6P_FAITH INP_FAITH +#define IN6P_CONTROLOPTS INP_CONTROLOPTS + /* + * socket AF version is {newer than,or include} + * actual datagram AF version + */ +#define INPLOOKUP_WILDCARD 1 #define sotoinpcb(so) ((struct inpcb *)(so)->so_pcb) +#define sotoin6pcb(so) sotoinpcb(so) /* for KAME src sync over BSD*'s */ + +#define INP_SOCKAF(so) so->so_proto->pr_domain->dom_family + +#define INP_CHECK_SOCKAF(so, af) (INP_SOCKAF(so) == af) #ifdef KERNEL +extern int ipport_lowfirstauto; +extern int ipport_lowlastauto; +extern int ipport_firstauto; +extern int ipport_lastauto; +extern int ipport_hifirstauto; +extern int ipport_hilastauto; + void in_losing __P((struct inpcb *)); int in_pcballoc __P((struct socket *, struct inpcbinfo *, struct proc *)); int in_pcbbind __P((struct inpcb *, struct sockaddr *, struct proc *)); diff --git a/sys/netinet/ip6.h b/sys/netinet/ip6.h new file mode 100644 index 0000000..a0fe4c1 --- /dev/null +++ b/sys/netinet/ip6.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index e153cc1..5b52c4e 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -66,8 +66,8 @@ #include #endif -static struct inpcbhead ripcb; -static struct inpcbinfo ripcbinfo; +struct inpcbhead ripcb; +struct inpcbinfo ripcbinfo; /* * Nominal space allocated to a raw ip socket. @@ -202,7 +202,7 @@ rip_output(m, so, dst) ip = mtod(m, struct ip *); /* don't allow both user specified and setsockopt options, and don't allow packet length sizes that will crash */ - if (((IP_VHL_HL(ip->ip_vhl) != (sizeof (*ip) >> 2)) + if (((IP_VHL_HL(ip->ip_vhl) != (sizeof (*ip) >> 2)) && inp->inp_options) || (ip->ip_len > m->m_pkthdr.len) || (ip->ip_len < (IP_VHL_HL(ip->ip_vhl) << 2))) { @@ -411,12 +411,12 @@ rip_ctlinput(cmd, sa, vip) } } -static u_long rip_sendspace = RIPSNDQ; -static u_long rip_recvspace = RIPRCVQ; +u_long rip_sendspace = RIPSNDQ; +u_long rip_recvspace = RIPRCVQ; -SYSCTL_INT(_net_inet_raw, OID_AUTO, maxdgram, CTLFLAG_RW, +SYSCTL_INT(_net_inet_raw, OID_AUTO, maxdgram, CTLFLAG_RW, &rip_sendspace, 0, "Maximum outgoing raw IP datagram size"); -SYSCTL_INT(_net_inet_raw, OID_AUTO, recvspace, CTLFLAG_RW, +SYSCTL_INT(_net_inet_raw, OID_AUTO, recvspace, CTLFLAG_RW, &rip_recvspace, 0, "Maximum incoming raw IP datagram size"); static int @@ -632,6 +632,6 @@ struct pr_usrreqs rip_usrreqs = { rip_abort, pru_accept_notsupp, rip_attach, rip_bind, rip_connect, pru_connect2_notsupp, in_control, rip_detach, rip_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, rip_send, pru_sense_null, rip_shutdown, + pru_rcvoob_notsupp, rip_send, pru_sense_null, rip_shutdown, in_setsockaddr, sosend, soreceive, sopoll }; diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 68c69b0..b472de5 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -35,6 +35,7 @@ */ #include "opt_compat.h" +#include "opt_inet.h" #include "opt_tcpdebug.h" #include diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index 68c69b0..b472de5 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -35,6 +35,7 @@ */ #include "opt_compat.h" +#include "opt_inet.h" #include "opt_tcpdebug.h" #include diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c new file mode 100644 index 0000000..403ed5d --- /dev/null +++ b/sys/netinet6/dest6.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* + * Destination options header processing. + */ +int +dest6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + register struct mbuf *m = *mp; + int off = *offp, dstoptlen, optlen; + struct ip6_dest *dstopts; + u_int8_t *opt; + + /* validation of the length of the header */ + IP6_EXTHDR_CHECK(m, off, sizeof(*dstopts), IPPROTO_DONE); + dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off); + dstoptlen = (dstopts->ip6d_len + 1) << 3; + + IP6_EXTHDR_CHECK(m, off, dstoptlen, IPPROTO_DONE); + dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off); + off += dstoptlen; + dstoptlen -= sizeof(struct ip6_dest); + opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest); + + /* search header for all options. */ + for (optlen = 0; dstoptlen > 0; dstoptlen -= optlen, opt += optlen) { + switch(*opt) { + case IP6OPT_PAD1: + optlen = 1; + break; + case IP6OPT_PADN: + if (dstoptlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + optlen = *(opt + 1) + 2; + break; + default: /* unknown option */ + if (dstoptlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if ((optlen = ip6_unknown_opt(opt, m, + opt-mtod(m, u_int8_t *))) == -1) + return(IPPROTO_DONE); + optlen += 2; + break; + } + } + + *offp = off; + return(dstopts->ip6d_nxt); + + bad: + m_freem(m); + return(IPPROTO_DONE); +} diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c new file mode 100644 index 0000000..390ab13e --- /dev/null +++ b/sys/netinet6/frag6.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* + * Define it to get a correct behavior on per-interface statistics. + * You will need to perform an extra routing table lookup, per fragment, + * to do it. This may, or may not be, a performance hit. + */ +#define IN6_IFSTAT_STRICT + +static void frag6_enq __P((struct ip6asfrag *, struct ip6asfrag *)); +static void frag6_deq __P((struct ip6asfrag *)); +static void frag6_insque __P((struct ip6q *, struct ip6q *)); +static void frag6_remque __P((struct ip6q *)); +static void frag6_freef __P((struct ip6q *)); + +int frag6_doing_reass; +u_int frag6_nfragpackets; +struct ip6q ip6q; /* ip6 reassemble queue */ + +#if !defined(M_FTABLE) +MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header"); +#endif + +/* + * Initialise reassembly queue and fragment identifier. + */ +void +frag6_init() +{ + struct timeval tv; + + /* + * in many cases, random() here does NOT return random number + * as initialization during bootstrap time occur in fixed order. + */ + microtime(&tv); + ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q; + ip6_id = random() ^ tv.tv_usec; +} + +/* + * Fragment input + */ +int +frag6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + struct mbuf *m = *mp, *t; + struct ip6_hdr *ip6; + struct ip6_frag *ip6f; + struct ip6q *q6; + struct ip6asfrag *af6, *ip6af; + int offset = *offp, nxt, i, next; + int first_frag = 0; + u_short fragoff, frgpartlen; + struct ifnet *dstifp; +#ifdef IN6_IFSTAT_STRICT + static struct route_in6 ro; + struct sockaddr_in6 *dst; +#endif + + IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); + + ip6 = mtod(m, struct ip6_hdr *); + ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); + + dstifp = NULL; +#ifdef IN6_IFSTAT_STRICT + /* find the destination interface of the packet. */ + dst = (struct sockaddr_in6 *)&ro.ro_dst; + if (ro.ro_rt + && ((ro.ro_rt->rt_flags & RTF_UP) == 0 + || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) { + RTFREE(ro.ro_rt); + ro.ro_rt = (struct rtentry *)0; + } + if (ro.ro_rt == NULL) { + bzero(dst, sizeof(*dst)); + dst->sin6_family = AF_INET6; + dst->sin6_len = sizeof(struct sockaddr_in6); + dst->sin6_addr = ip6->ip6_dst; + } + rtcalloc((struct route *)&ro); + if (ro.ro_rt != NULL && ro.ro_rt->rt_ifa != NULL) + dstifp = ((struct in6_ifaddr *)ro.ro_rt->rt_ifa)->ia_ifp; +#else + /* we are violating the spec, this is not the destination interface */ + if ((m->m_flags & M_PKTHDR) != 0) + dstifp = m->m_pkthdr.rcvif; +#endif + + /* jumbo payload can't contain a fragment header */ + if (ip6->ip6_plen == 0) { + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset); + in6_ifstat_inc(dstifp, ifs6_reass_fail); + return IPPROTO_DONE; + } + + /* + * check whether fragment packet's fragment length is + * multiple of 8 octets. + * sizeof(struct ip6_frag) == 8 + * sizeof(struct ip6_hdr) = 40 + */ + if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && + (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); + in6_ifstat_inc(dstifp, ifs6_reass_fail); + return IPPROTO_DONE; + } + + ip6stat.ip6s_fragments++; + in6_ifstat_inc(dstifp, ifs6_reass_reqd); + + /* + * Presence of header sizes in mbufs + * would confuse code below. + */ + + offset += sizeof(struct ip6_frag); + m->m_data += offset; + m->m_len -= offset; + + for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) + if (ip6f->ip6f_ident == q6->ip6q_ident && + IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst)) + break; + + if (q6 == &ip6q) { + /* + * the first fragment to arrive, create a reassembly queue. + */ + first_frag = 1; + frag6_nfragpackets++; + + /* + * Enforce upper bound on number of fragmented packets + * for which we attempt reassembly; + * If maxfrag is 0, never accept fragments. + * If maxfrag is -1, accept all fragments without limitation. + */ + if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) { + ip6stat.ip6s_fragoverflow++; + in6_ifstat_inc(dstifp, ifs6_reass_fail); + frag6_freef(ip6q.ip6q_prev); + } + q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, + M_DONTWAIT); + if (q6 == NULL) + goto dropfrag; + + frag6_insque(q6, &ip6q); + + q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; + q6->ip6q_ident = ip6f->ip6f_ident; + q6->ip6q_arrive = 0; /* Is it used anywhere? */ + q6->ip6q_ttl = IPV6_FRAGTTL; + q6->ip6q_src = ip6->ip6_src; + q6->ip6q_dst = ip6->ip6_dst; + q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ + } + + /* + * If it's the 1st fragment, record the length of the + * unfragmentable part and the next header of the fragment header. + */ + fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); + if (fragoff == 0) { + q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) + - sizeof(struct ip6_frag); + q6->ip6q_nxt = ip6f->ip6f_nxt; + } + + /* + * Check that the reassembled packet would not exceed 65535 bytes + * in size. + * If it would exceed, discard the fragment and return an ICMP error. + */ + frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; + if (q6->ip6q_unfrglen >= 0) { + /* The 1st fragment has already arrived. */ + if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) { + m->m_data -= offset; + m->m_len += offset; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, + offset - sizeof(struct ip6_frag) + 2); + return(IPPROTO_DONE); + } + } + else if (fragoff + frgpartlen > IPV6_MAXPACKET) { + m->m_data -= offset; + m->m_len += offset; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, + offset - sizeof(struct ip6_frag) + 2); + return(IPPROTO_DONE); + } + /* + * If it's the first fragment, do the above check for each + * fragment already stored in the reassembly queue. + */ + if (fragoff == 0) { + struct ip6asfrag *af6dwn; + + for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; + af6 = af6dwn) { + af6dwn = af6->ip6af_down; + + if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen > + IPV6_MAXPACKET) { + struct mbuf *merr = IP6_REASS_MBUF(af6); + struct ip6_hdr *ip6err; + int erroff = af6->ip6af_offset; + + /* dequeue the fragment. */ + frag6_deq(af6); + + /* adjust pointer. */ + merr->m_data -= af6->ip6af_offset; + merr->m_len += af6->ip6af_offset; + ip6err = mtod(merr, struct ip6_hdr *); + + /* + * Restore source and destination addresses + * in the erroneous IPv6 header. + */ + ip6err->ip6_src = q6->ip6q_src; + ip6err->ip6_dst = q6->ip6q_dst; + + icmp6_error(merr, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + erroff - sizeof(struct ip6_frag) + 2); + } + } + } + + /* Override the IPv6 header */ + ip6af = (struct ip6asfrag *)ip6; + ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG; + ip6af->ip6af_off = fragoff; + ip6af->ip6af_frglen = frgpartlen; + ip6af->ip6af_offset = offset; + IP6_REASS_MBUF(ip6af) = m; + + if (first_frag) { + af6 = (struct ip6asfrag *)q6; + goto insert; + } + + /* + * Find a segment which begins after this one does. + */ + for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; + af6 = af6->ip6af_down) + if (af6->ip6af_off > ip6af->ip6af_off) + break; + + /* + * If the incoming framgent overlaps some existing fragments in + * the reassembly queue, drop it, since it is dangerous to override + * existing fragments from a security point of view. + */ + if (af6->ip6af_up != (struct ip6asfrag *)q6) { + i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen + - ip6af->ip6af_off; + if (i > 0) { + log(LOG_ERR, "%d bytes of a fragment from %s " + "overlaps the previous fragment\n", + i, ip6_sprintf(&q6->ip6q_src)); + goto dropfrag; + } + } + if (af6 != (struct ip6asfrag *)q6) { + i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; + if (i > 0) { + log(LOG_ERR, "%d bytes of a fragment from %s " + "overlaps the succeeding fragment", + i, ip6_sprintf(&q6->ip6q_src)); + goto dropfrag; + } + } + +insert: + + /* + * Stick new segment in its place; + * check for complete reassembly. + * Move to front of packet queue, as we are + * the most recently active fragmented packet. + */ + frag6_enq(ip6af, af6->ip6af_up); + next = 0; + for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; + af6 = af6->ip6af_down) { + if (af6->ip6af_off != next) { + frag6_doing_reass = 0; + return IPPROTO_DONE; + } + next += af6->ip6af_frglen; + } + if (af6->ip6af_up->ip6af_mff) { + frag6_doing_reass = 0; + return IPPROTO_DONE; + } + + /* + * Reassembly is complete; concatenate fragments. + */ + + ip6af = q6->ip6q_down; + t = m = IP6_REASS_MBUF(ip6af); + af6 = ip6af->ip6af_down; + while (af6 != (struct ip6asfrag *)q6) { + while (t->m_next) + t = t->m_next; + t->m_next = IP6_REASS_MBUF(af6); + af6 = af6->ip6af_down; + } + + /* adjust offset to point where the original next header starts */ + offset = ip6af->ip6af_offset - sizeof(struct ip6_frag); + ip6 = (struct ip6_hdr *)ip6af; + ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr)); + ip6->ip6_src = q6->ip6q_src; + ip6->ip6_dst = q6->ip6q_dst; + nxt = q6->ip6q_nxt; + + /* + * Delete frag6 header with as a few cost as possible. + */ + + if (offset < m->m_len) + ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag), + offset); + else { + ovbcopy(mtod(m, caddr_t), (caddr_t)ip6 + offset, m->m_len); + m->m_data -= sizeof(struct ip6_frag); + } + m->m_data -= offset; + m->m_len += offset; + + /* + * Store NXT to the original. + */ + { + char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */ + *prvnxtp = nxt; + } + + frag6_remque(q6); + free(q6, M_FTABLE); + frag6_nfragpackets--; + + if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */ + int plen = 0; + for (t = m; t; t = t->m_next) + plen += t->m_len; + m->m_pkthdr.len = plen; + } + + ip6stat.ip6s_reassembled++; + in6_ifstat_inc(dstifp, ifs6_reass_ok); + + /* + * Tell launch routine the next header + */ + + *mp = m; + *offp = offset; + + frag6_doing_reass = 0; + return nxt; + + dropfrag: + in6_ifstat_inc(dstifp, ifs6_reass_fail); + ip6stat.ip6s_fragdropped++; + m_freem(m); + return IPPROTO_DONE; +} + +/* + * Free a fragment reassembly header and all + * associated datagrams. + */ +void +frag6_freef(q6) + struct ip6q *q6; +{ + struct ip6asfrag *af6, *down6; + + for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; + af6 = down6) { + struct mbuf *m = IP6_REASS_MBUF(af6); + + down6 = af6->ip6af_down; + frag6_deq(af6); + + /* + * Return ICMP time exceeded error for the 1st fragment. + * Just free other fragments. + */ + if (af6->ip6af_off == 0) { + struct ip6_hdr *ip6; + + /* adjust pointer */ + m->m_data -= af6->ip6af_offset; + m->m_len += af6->ip6af_offset; + ip6 = mtod(m, struct ip6_hdr *); + + /* restoure source and destination addresses */ + ip6->ip6_src = q6->ip6q_src; + ip6->ip6_dst = q6->ip6q_dst; + + icmp6_error(m, ICMP6_TIME_EXCEEDED, + ICMP6_TIME_EXCEED_REASSEMBLY, 0); + } + else + m_freem(m); + } + frag6_remque(q6); + free(q6, M_FTABLE); + frag6_nfragpackets--; +} + +/* + * Put an ip fragment on a reassembly chain. + * Like insque, but pointers in middle of structure. + */ +void +frag6_enq(af6, up6) + struct ip6asfrag *af6, *up6; +{ + af6->ip6af_up = up6; + af6->ip6af_down = up6->ip6af_down; + up6->ip6af_down->ip6af_up = af6; + up6->ip6af_down = af6; +} + +/* + * To frag6_enq as remque is to insque. + */ +void +frag6_deq(af6) + struct ip6asfrag *af6; +{ + af6->ip6af_up->ip6af_down = af6->ip6af_down; + af6->ip6af_down->ip6af_up = af6->ip6af_up; +} + +void +frag6_insque(new, old) + struct ip6q *new, *old; +{ + new->ip6q_prev = old; + new->ip6q_next = old->ip6q_next; + old->ip6q_next->ip6q_prev= new; + old->ip6q_next = new; +} + +void +frag6_remque(p6) + struct ip6q *p6; +{ + p6->ip6q_prev->ip6q_next = p6->ip6q_next; + p6->ip6q_next->ip6q_prev = p6->ip6q_prev; +} + +/* + * IP timer processing; + * if a timer expires on a reassembly + * queue, discard it. + */ +void +frag6_slowtimo() +{ + struct ip6q *q6; + int s = splnet(); + + frag6_doing_reass = 1; + q6 = ip6q.ip6q_next; + if (q6) + while (q6 != &ip6q) { + --q6->ip6q_ttl; + q6 = q6->ip6q_next; + if (q6->ip6q_prev->ip6q_ttl == 0) { + ip6stat.ip6s_fragtimeout++; + /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ + frag6_freef(q6->ip6q_prev); + } + } + /* + * If we are over the maximum number of fragments + * (due to the limit being lowered), drain off + * enough to get down to the new limit. + */ + while (frag6_nfragpackets > (u_int)ip6_maxfragpackets) { + ip6stat.ip6s_fragoverflow++; + /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ + frag6_freef(ip6q.ip6q_prev); + } + frag6_doing_reass = 0; + splx(s); +} + +/* + * Drain off all datagram fragments. + */ +void +frag6_drain() +{ + if (frag6_doing_reass) + return; + while (ip6q.ip6q_next != &ip6q) { + ip6stat.ip6s_fragdropped++; + /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ + frag6_freef(ip6q.ip6q_next); + } +} diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c new file mode 100644 index 0000000..e06c6ef --- /dev/null +++ b/sys/netinet6/icmp6.c @@ -0,0 +1,1868 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 + */ + +#include "opt_inet.h" +#include "opt_ipsec.h" +#include "opt_key.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef IPSEC +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#include +#ifdef KEY_DEBUG +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#else +#define DPRINTF(lev,arg) +#define DDO(lev, stmt) +#define DP(x, y, z) +#endif /* KEY_DEBUG */ +#endif /* IPSEC */ + +/* #include "faith.h" */ + +#include + +extern struct domain inet6domain; +extern struct ip6protosw inet6sw[]; +extern u_char ip6_protox[]; + +struct icmp6stat icmp6stat; + +extern struct inpcbhead ripcb; +extern u_int icmp6errratelim; + +static int icmp6_rip6_input __P((struct mbuf **, int)); +static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); +static const char *icmp6_redirect_diag __P((struct in6_addr *, + struct in6_addr *, + struct in6_addr *)); +static struct mbuf *ni6_input __P((struct mbuf *, int)); +static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, + struct ifnet **)); +static int ni6_store_addrs __P((struct icmp6_nodeinfo *, + struct icmp6_nodeinfo *, + struct ifnet *, int)); + +#ifdef COMPAT_RFC1885 +static struct route_in6 icmp6_reflect_rt; +#endif +static struct timeval icmp6_nextsend = {0, 0}; + +void +icmp6_init() +{ + mld6_init(); +} + +/* + * Generate an error packet of type error in response to bad IP6 packet. + */ +void +icmp6_error(m, type, code, param) + struct mbuf *m; + int type, code, param; +{ + struct ip6_hdr *oip6, *nip6; + struct icmp6_hdr *icmp6; + u_int prep; + int off; + u_char nxt; + + icmp6stat.icp6s_error++; + + if (m->m_flags & M_DECRYPTED) + goto freeit; + + oip6 = mtod(m, struct ip6_hdr *); + + /* + * Multicast destination check. For unrecognized option errors, + * this check has already done in ip6_unknown_opt(), so we can + * check only for other errors. + */ + if ((m->m_flags & (M_BCAST|M_MCAST) || + IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) && + (type != ICMP6_PACKET_TOO_BIG && + (type != ICMP6_PARAM_PROB || + code != ICMP6_PARAMPROB_OPTION))) + goto freeit; + + /* Source address check. XXX: the case of anycast source? */ + if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) || + IN6_IS_ADDR_MULTICAST(&oip6->ip6_src)) + goto freeit; + + /* + * If the erroneous packet is also an ICMP error, discard it. + */ + IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); + off = sizeof(struct ip6_hdr); + nxt = oip6->ip6_nxt; + while(1) { /* XXX: should avoid inf. loop explicitly? */ + struct ip6_ext *ip6e; + struct icmp6_hdr *icp; + + switch(nxt) { + case IPPROTO_IPV6: + case IPPROTO_IPV4: + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ESP: + case IPPROTO_FRAGMENT: + /* + * ICMPv6 error must not be fragmented. + * XXX: but can we trust the sender? + */ + default: + /* What if unknown header followed by ICMP error? */ + goto generate; + case IPPROTO_ICMPV6: + IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); + icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); + if (icp->icmp6_type < ICMP6_ECHO_REQUEST + || icp->icmp6_type == ND_REDIRECT) { + /* + * ICMPv6 error + * Special case: for redirect (which is + * informational) we must not send icmp6 error. + */ + icmp6stat.icp6s_canterror++; + goto freeit; + } else { + /* ICMPv6 informational */ + goto generate; + } + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_AH: + IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), ); + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); + if (nxt == IPPROTO_AH) + off += (ip6e->ip6e_len + 2) << 2; + else + off += (ip6e->ip6e_len + 1) << 3; + nxt = ip6e->ip6e_nxt; + break; + } + } + + freeit: + /* + * If we can't tell wheter or not we can generate ICMP6, free it. + */ + m_freem(m); + return; + + generate: + oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ + + /* Finally, do rate limitation check. */ + if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + + /* + * OK, ICMP6 can be generated. + */ + + if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) + m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); + + prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); + M_PREPEND(m, prep, M_DONTWAIT); + if (m && m->m_len < prep) + m = m_pullup(m, prep); + if (m == NULL) { + printf("ENOBUFS in icmp6_error %d\n", __LINE__); + return; + } + + nip6 = mtod(m, struct ip6_hdr *); + nip6->ip6_src = oip6->ip6_src; + nip6->ip6_dst = oip6->ip6_dst; + + if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) + oip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) + oip6->ip6_dst.s6_addr16[1] = 0; + + icmp6 = (struct icmp6_hdr *)(nip6 + 1); + icmp6->icmp6_type = type; + icmp6->icmp6_code = code; + icmp6->icmp6_pptr = htonl((u_int32_t)param); + + icmp6stat.icp6s_outhist[type]++; + icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/ +} + +/* + * Process a received ICMP6 message. + */ +int +icmp6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + struct mbuf *m = *mp, *n; + struct ip6_hdr *ip6, *nip6; + struct icmp6_hdr *icmp6, *nicmp6; + int off = *offp; + int icmp6len = m->m_pkthdr.len - *offp; + int code, sum, noff; + struct sockaddr_in6 icmp6src; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); + /* m might change if M_LOOP. So, call mtod after this */ + + /* + * Locate icmp6 structure in mbuf, and check + * that not corrupted and of at least minimum length + */ + + ip6 = mtod(m, struct ip6_hdr *); + if (icmp6len < sizeof(struct icmp6_hdr)) { + icmp6stat.icp6s_tooshort++; + goto freeit; + } + + /* + * calculate the checksum + */ + + icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); + code = icmp6->icmp6_code; + + if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { + log(LOG_ERR, + "ICMP6 checksum error(%d|%x) %s\n", + icmp6->icmp6_type, + sum, + ip6_sprintf(&ip6->ip6_src)); + icmp6stat.icp6s_checksum++; + goto freeit; + } + +#if defined(NFAITH) && 0 < NFAITH + if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { + /* + * Deliver very specific ICMP6 type only. + * This is important to deilver TOOBIG. Otherwise PMTUD + * will not work. + */ + switch (icmp6->icmp6_type) { + case ICMP6_DST_UNREACH: + case ICMP6_PACKET_TOO_BIG: + case ICMP6_TIME_EXCEEDED: + break; + default: + goto freeit; + } + } +#endif + +#ifdef IPSEC + /* drop it if it does not match the default policy */ + if (ipsec6_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto freeit; + } +#endif + + icmp6stat.icp6s_inhist[icmp6->icmp6_type]++; + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg); + if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error); + + switch (icmp6->icmp6_type) { + + case ICMP6_DST_UNREACH: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + code = PRC_UNREACH_NET; + break; + case ICMP6_DST_UNREACH_ADMIN: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib); + case ICMP6_DST_UNREACH_ADDR: + code = PRC_UNREACH_HOST; + break; + case ICMP6_DST_UNREACH_NOTNEIGHBOR: + code = PRC_UNREACH_SRCFAIL; + break; + case ICMP6_DST_UNREACH_NOPORT: + code = PRC_UNREACH_PORT; + break; + default: + goto badcode; + } + goto deliver; + break; + + case ICMP6_PACKET_TOO_BIG: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig); + if (code != 0) + goto badcode; + { + u_int mtu = ntohl(icmp6->icmp6_mtu); + struct rtentry *rt = NULL; + struct sockaddr_in6 sin6; + + code = PRC_MSGSIZE; + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = PF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + rt = rtalloc1((struct sockaddr *)&sin6, 0, + RTF_CLONING | RTF_PRCLONING); + if (rt && (rt->rt_flags & RTF_HOST) + && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { + if (mtu < IPV6_MMTU) { + /* xxx */ + rt->rt_rmx.rmx_locks |= RTV_MTU; + } else if (mtu < rt->rt_ifp->if_mtu && + rt->rt_rmx.rmx_mtu > mtu) { + rt->rt_rmx.rmx_mtu = mtu; + } + } + if (rt) + RTFREE(rt); + + goto deliver; + } + break; + + case ICMP6_TIME_EXCEEDED: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed); + switch (code) { + case ICMP6_TIME_EXCEED_TRANSIT: + case ICMP6_TIME_EXCEED_REASSEMBLY: + code += PRC_TIMXCEED_INTRANS; + break; + default: + goto badcode; + } + goto deliver; + break; + + case ICMP6_PARAM_PROB: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob); + switch (code) { + case ICMP6_PARAMPROB_NEXTHEADER: + code = PRC_UNREACH_PROTOCOL; + break; + case ICMP6_PARAMPROB_HEADER: + case ICMP6_PARAMPROB_OPTION: + code = PRC_PARAMPROB; + break; + default: + goto badcode; + } + goto deliver; + break; + + case ICMP6_ECHO_REQUEST: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo); + if (code != 0) + goto badcode; + if ((n = m_copy(m, 0, M_COPYALL)) == NULL) { + /* Give up remote */ + break; + } + if (n->m_flags & M_EXT) { + int gap, move; + struct mbuf *n0 = n; + + /* + * Prepare an internal mbuf. m_pullup() doesn't + * always copy the length we specified. + */ + MGETHDR(n, M_DONTWAIT, n0->m_type); + if (n == NULL) { + /* Give up remote */ + m_freem(n0); + break; + } + M_COPY_PKTHDR(n, n0); + n0->m_flags &= ~M_PKTHDR; + n->m_next = n0; + /* + * Copy IPv6 and ICMPv6 only. + */ + nip6 = mtod(n, struct ip6_hdr *); + bcopy(ip6, nip6, sizeof(struct ip6_hdr)); + nicmp6 = (struct icmp6_hdr *)(nip6 + 1); + bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); + /* + * Adjust mbuf. ip6_plen will be adjusted. + */ + noff = sizeof(struct ip6_hdr); + n->m_len = noff + sizeof(struct icmp6_hdr); + move = off + sizeof(struct icmp6_hdr); + n0->m_len -= move; + n0->m_data += move; + gap = off - noff; + n->m_pkthdr.len -= gap; + } else { + nip6 = mtod(n, struct ip6_hdr *); + nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off); + noff = off; + } + nicmp6->icmp6_type = ICMP6_ECHO_REPLY; + nicmp6->icmp6_code = 0; + if (n) { + icmp6stat.icp6s_reflect++; + icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++; + icmp6_reflect(n, noff); + } + break; + + case ICMP6_ECHO_REPLY: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply); + if (code != 0) + goto badcode; + break; + + case MLD6_LISTENER_QUERY: + case MLD6_LISTENER_REPORT: + if (icmp6len < sizeof(struct mld6_hdr)) + goto badlen; + if (icmp6->icmp6_type == MLD6_LISTENER_QUERY) /* XXX: ugly... */ + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery); + else + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport); + IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); + mld6_input(m, off); + /* m stays. */ + break; + + case MLD6_LISTENER_DONE: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone); + if (icmp6len < sizeof(struct mld6_hdr)) /* necessary? */ + goto badlen; + break; /* nothing to be done in kernel */ + + case MLD6_MTRACE_RESP: + case MLD6_MTRACE: + /* XXX: these two are experimental. not officially defind. */ + /* XXX: per-interface statistics? */ + break; /* just pass it to the userland daemon */ + + case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ + { + enum { WRU, FQDN } mode; + + if (code != 0) + goto badcode; + if (icmp6len == sizeof(struct icmp6_hdr) + 4) + mode = WRU; + else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */ + mode = FQDN; + else + goto badlen; + +#define hostnamelen strlen(hostname) + if (mode == FQDN) { + IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), + IPPROTO_DONE); + n = ni6_input(m, off); + noff = sizeof(struct ip6_hdr); + } else { + u_char *p; + + MGETHDR(n, M_DONTWAIT, m->m_type); + if (n == NULL) { + /* Give up remote */ + break; + } + /* + * Copy IPv6 and ICMPv6 only. + */ + nip6 = mtod(n, struct ip6_hdr *); + bcopy(ip6, nip6, sizeof(struct ip6_hdr)); + nicmp6 = (struct icmp6_hdr *)(nip6 + 1); + bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); + p = (u_char *)(nicmp6 + 1); + bzero(p, 4); + bcopy(hostname, p + 4, hostnamelen); + noff = sizeof(struct ip6_hdr); + M_COPY_PKTHDR(n, m); /* just for recvif */ + n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + + sizeof(struct icmp6_hdr) + 4 + hostnamelen; + nicmp6->icmp6_type = ICMP6_WRUREPLY; + nicmp6->icmp6_code = 0; + } +#undef hostnamelen + if (n) { + icmp6stat.icp6s_reflect++; + icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++; + icmp6_reflect(n, noff); + } + break; + } + + case ICMP6_WRUREPLY: + if (code != 0) + goto badcode; + break; + + case ND_ROUTER_SOLICIT: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit); + if (code != 0) + goto badcode; + if (icmp6len < sizeof(struct nd_router_solicit)) + goto badlen; + IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); + nd6_rs_input(m, off, icmp6len); + /* m stays. */ + break; + + case ND_ROUTER_ADVERT: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert); + if (code != 0) + goto badcode; + if (icmp6len < sizeof(struct nd_router_advert)) + goto badlen; + IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); + nd6_ra_input(m, off, icmp6len); + /* m stays. */ + break; + + case ND_NEIGHBOR_SOLICIT: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit); + if (code != 0) + goto badcode; + if (icmp6len < sizeof(struct nd_neighbor_solicit)) + goto badlen; + IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); + nd6_ns_input(m, off, icmp6len); + /* m stays. */ + break; + + case ND_NEIGHBOR_ADVERT: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert); + if (code != 0) + goto badcode; + if (icmp6len < sizeof(struct nd_neighbor_advert)) + goto badlen; + IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); + nd6_na_input(m, off, icmp6len); + /* m stays. */ + break; + + case ND_REDIRECT: + icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect); + if (code != 0) + goto badcode; + if (icmp6len < sizeof(struct nd_redirect)) + goto badlen; + icmp6_redirect_input(m, off); + /* m stays. */ + break; + + case ICMP6_ROUTER_RENUMBERING: + if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && + code != ICMP6_ROUTER_RENUMBERING_RESULT) + goto badcode; + if (icmp6len < sizeof(struct icmp6_router_renum)) + goto badlen; + break; + + default: + printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", + icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0); + if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { + /* ICMPv6 error: MUST deliver it by spec... */ + code = PRC_NCMDS; + /* deliver */ + } else { + /* ICMPv6 informational: MUST not deliver */ + break; + } + deliver: + if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { + icmp6stat.icp6s_tooshort++; + goto freeit; + } + IP6_EXTHDR_CHECK(m, off, + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), + IPPROTO_DONE); + icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); + bzero(&icmp6src, sizeof(icmp6src)); + icmp6src.sin6_len = sizeof(struct sockaddr_in6); + icmp6src.sin6_family = AF_INET6; + icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + + /* Detect the upper level protocol */ + { + void (*ctlfunc) __P((int, struct sockaddr *, void *)); + struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1); + u_int8_t nxt = eip6->ip6_nxt; + int eoff = off + sizeof(struct icmp6_hdr) + + sizeof(struct ip6_hdr); + struct ip6ctlparam ip6cp; + + while (1) { /* XXX: should avoid inf. loop explicitly? */ + struct ip6_ext *eh; + + switch(nxt) { + case IPPROTO_ESP: + case IPPROTO_NONE: + goto passit; + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_AH: + case IPPROTO_FRAGMENT: + IP6_EXTHDR_CHECK(m, 0, eoff + + sizeof(struct ip6_ext), + IPPROTO_DONE); + eh = (struct ip6_ext *)(mtod(m, caddr_t) + + eoff); + if (nxt == IPPROTO_AH) + eoff += (eh->ip6e_len + 2) << 2; + else if (nxt == IPPROTO_FRAGMENT) + eoff += sizeof(struct ip6_frag); + else + eoff += (eh->ip6e_len + 1) << 3; + nxt = eh->ip6e_nxt; + break; + default: + goto notify; + } + } + notify: + icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); + ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) + (inet6sw[ip6_protox[nxt]].pr_ctlinput); + if (ctlfunc) { + ip6cp.ip6c_m = m; + ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); + ip6cp.ip6c_off = eoff; + (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp); + } + } + break; + + badcode: + icmp6stat.icp6s_badcode++; + break; + + badlen: + icmp6stat.icp6s_badlen++; + break; + } + + passit: + icmp6_rip6_input(&m, *offp); + return IPPROTO_DONE; + + freeit: + m_freem(m); + return IPPROTO_DONE; +} + +/* + * Process a Node Information Query + */ +#define hostnamelen strlen(hostname) +#ifndef offsetof /* XXX */ +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) +#endif + +static struct mbuf * +ni6_input(m, off) + struct mbuf *m; + int off; +{ + struct icmp6_nodeinfo *ni6 = + (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off), *nni6; + struct mbuf *n = NULL; + u_int16_t qtype = ntohs(ni6->ni_qtype); + int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); + struct ni_reply_fqdn *fqdn; + int addrs; /* for NI_QTYPE_NODEADDR */ + struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ + + switch(qtype) { + case NI_QTYPE_NOOP: + break; /* no reply data */ + case NI_QTYPE_SUPTYPES: + goto bad; /* xxx: to be implemented */ + break; + case NI_QTYPE_FQDN: + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + + hostnamelen; + break; + case NI_QTYPE_NODEADDR: + addrs = ni6_addrs(ni6, m, &ifp); + if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) + replylen = MCLBYTES; /* XXX: we'll truncate later */ + + break; + default: + /* + * XXX: We must return a reply with the ICMP6 code + * `unknown Qtype' in this case. However we regard the case + * as an FQDN query for backward compatibility. + * Older versions set a random value to this field, + * so it rarely varies in the defined qtypes. + * But the mechanism is not reliable... + * maybe we should obsolete older versions. + */ + qtype = NI_QTYPE_FQDN; + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + + hostnamelen; + break; + } + + /* allocate a mbuf to reply. */ + MGETHDR(n, M_DONTWAIT, m->m_type); + if (n == NULL) + return(NULL); + M_COPY_PKTHDR(n, m); /* just for recvif */ + if (replylen > MHLEN) { + if (replylen > MCLBYTES) + /* + * XXX: should we try to allocate more? But MCLBYTES is + * probably much larger than IPV6_MMTU... + */ + goto bad; + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + goto bad; + } + } + n->m_pkthdr.len = n->m_len = replylen; + + /* copy mbuf header and IPv6 + Node Information base headers */ + bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr)); + nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1); + bcopy(mtod(m, caddr_t) + off, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); + + /* qtype dependent procedure */ + switch (qtype) { + case NI_QTYPE_NOOP: + nni6->ni_flags = 0; + break; + case NI_QTYPE_SUPTYPES: + goto bad; /* xxx: to be implemented */ + break; + case NI_QTYPE_FQDN: + if (hostnamelen > 255) { /* XXX: rare case, but may happen */ + printf("ni6_input: " + "hostname length(%d) is too large for reply\n", + hostnamelen); + goto bad; + } + fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + + sizeof(struct ip6_hdr) + + sizeof(struct icmp6_nodeinfo)); + nni6->ni_flags = 0; /* XXX: meaningless TTL */ + fqdn->ni_fqdn_ttl = 0; /* ditto. */ + fqdn->ni_fqdn_namelen = hostnamelen; + bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen); + break; + case NI_QTYPE_NODEADDR: + { + int lenlim, copied; + + if (n->m_flags & M_EXT) + lenlim = MCLBYTES - sizeof(struct ip6_hdr) - + sizeof(struct icmp6_nodeinfo); + else + lenlim = MHLEN - sizeof(struct ip6_hdr) - + sizeof(struct icmp6_nodeinfo); + copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); + /* XXX: reset mbuf length */ + n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + + sizeof(struct icmp6_nodeinfo) + copied; + break; + } + default: + break; /* XXX impossible! */ + } + + nni6->ni_type = ICMP6_NI_REPLY; + nni6->ni_code = ICMP6_NI_SUCESS; + return(n); + + bad: + if (n) + m_freem(n); + return(NULL); +} +#undef hostnamelen + +/* + * calculate the number of addresses to be returned in the node info reply. + */ +static int +ni6_addrs(ni6, m, ifpp) + struct icmp6_nodeinfo *ni6; + struct mbuf *m; + struct ifnet **ifpp; +{ + register struct ifnet *ifp; + register struct in6_ifaddr *ifa6; + register struct ifaddr *ifa; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + int addrs = 0, addrsofif, iffound = 0; + + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) + { + addrsofif = 0; + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifa6 = (struct in6_ifaddr *)ifa; + + if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) && + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + &ifa6->ia_addr.sin6_addr)) + iffound = 1; + + if (ifa6->ia6_flags & IN6_IFF_ANYCAST) + continue; /* we need only unicast addresses */ + + if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL | + NI_NODEADDR_FLAG_SITELOCAL | + NI_NODEADDR_FLAG_GLOBAL)) == 0) + continue; + + /* What do we have to do about ::1? */ + switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + case IPV6_ADDR_SCOPE_LINKLOCAL: + if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) + addrsofif++; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) + addrsofif++; + break; + case IPV6_ADDR_SCOPE_GLOBAL: + if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) + addrsofif++; + break; + default: + continue; + } + } + if (iffound) { + *ifpp = ifp; + return(addrsofif); + } + + addrs += addrsofif; + } + + return(addrs); +} + +static int +ni6_store_addrs(ni6, nni6, ifp0, resid) + struct icmp6_nodeinfo *ni6, *nni6; + struct ifnet *ifp0; + int resid; +{ + register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); + register struct in6_ifaddr *ifa6; + register struct ifaddr *ifa; + int docopy, copied = 0; + u_char *cp = (u_char *)(nni6 + 1); + + if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL)) + return(0); /* needless to copy */ + + for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) + { + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) + { + docopy = 0; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifa6 = (struct in6_ifaddr *)ifa; + + if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { + /* just experimental. not in the spec. */ + if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) + docopy = 1; + else + continue; + } else { /* unicast address */ + if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) + continue; + else + docopy = 1; + } + + /* What do we have to do about ::1? */ + switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + case IPV6_ADDR_SCOPE_LINKLOCAL: + if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) + docopy = 1; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) + docopy = 1; + break; + case IPV6_ADDR_SCOPE_GLOBAL: + if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) + docopy = 1; + break; + default: + continue; + } + + if (docopy) { + if (resid < sizeof(struct in6_addr)) { + /* + * We give up much more copy. + * Set the truncate flag and return. + */ + nni6->ni_flags |= + NI_NODEADDR_FLAG_TRUNCATE; + return(copied); + } + bcopy(&ifa6->ia_addr.sin6_addr, cp, + sizeof(struct in6_addr)); + /* XXX: KAME link-local hack; remove ifindex */ + if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) + ((struct in6_addr *)cp)->s6_addr16[1] = 0; + cp += sizeof(struct in6_addr); + resid -= sizeof(struct in6_addr); + copied += sizeof(struct in6_addr); + } + } + if (ifp0) /* we need search only on the specified IF */ + break; + } + + return(copied); +} + +/* + * XXX almost dup'ed code with rip6_input. + */ +static int +icmp6_rip6_input(mp, off) + struct mbuf **mp; + int off; +{ + struct mbuf *m = *mp; + register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + register struct in6pcb *in6p; + struct in6pcb *last = NULL; + struct sockaddr_in6 rip6src; + struct icmp6_hdr *icmp6; + struct mbuf *opts = NULL; + + /* this is assumed to be safe. */ + icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); + + bzero(&rip6src, sizeof(rip6src)); + rip6src.sin6_len = sizeof(struct sockaddr_in6); + rip6src.sin6_family = AF_INET6; + rip6src.sin6_addr = ip6->ip6_src; + if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) + rip6src.sin6_addr.s6_addr16[1] = 0; + if (m->m_pkthdr.rcvif) { + if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) + rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index; + else + rip6src.sin6_scope_id = 0; + } else + rip6src.sin6_scope_id = 0; + + LIST_FOREACH(in6p, &ripcb, inp_list) + { + if ((in6p->inp_vflag & INP_IPV6) == NULL) + continue; + if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && + !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && + !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) + continue; + if (in6p->in6p_icmp6filt + && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, + in6p->in6p_icmp6filt)) + continue; + if (last) { + struct mbuf *n; + if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { + if (last->in6p_flags & IN6P_CONTROLOPTS) + ip6_savecontrol(last, &opts, ip6, n); + /* strip intermediate headers */ + m_adj(n, off); + if (sbappendaddr(&last->in6p_socket->so_rcv, + (struct sockaddr *)&rip6src, + n, opts) == 0) { + /* should notify about lost packet */ + m_freem(n); + if (opts) + m_freem(opts); + } else + sorwakeup(last->in6p_socket); + opts = NULL; + } + } + last = in6p; + } + if (last) { + if (last->in6p_flags & IN6P_CONTROLOPTS) + ip6_savecontrol(last, &opts, ip6, m); + /* strip intermediate headers */ + m_adj(m, off); + if (sbappendaddr(&last->in6p_socket->so_rcv, + (struct sockaddr *)&rip6src, m, opts) == 0) { + m_freem(m); + if (opts) + m_freem(opts); + } else + sorwakeup(last->in6p_socket); + } else { + m_freem(m); + ip6stat.ip6s_delivered--; + } + return IPPROTO_DONE; +} + +/* + * Reflect the ip6 packet back to the source. + * The caller MUST check if the destination is multicast or not. + * This function is usually called with a unicast destination which + * can be safely the source of the reply packet. But some exceptions + * exist(e.g. ECHOREPLY, PATCKET_TOOBIG, "10" in OPTION type). + * ``off'' points to the icmp6 header, counted from the top of the mbuf. + */ +void +icmp6_reflect(m, off) + struct mbuf *m; + size_t off; +{ + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct icmp6_hdr *icmp6; + struct in6_ifaddr *ia; + struct in6_addr t, *src = 0; + int plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); + int type, code; + struct ifnet *outif = NULL; +#ifdef COMPAT_RFC1885 + int mtu = IPV6_MMTU; + struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; +#endif + + /* + * If there are extra headers between IPv6 and ICMPv6, strip + * off that header first. + */ + if (off != sizeof(struct ip6_hdr)) { + size_t siz; + + /* sanity checks */ + if (off < sizeof(struct ip6_hdr)) { + printf("sanity fail: off=%x, sizeof(ip6)=%x in %s:%d\n", + (unsigned int)off, + (unsigned int)sizeof(struct ip6_hdr), + __FILE__, __LINE__); + goto bad; + } + siz = off - sizeof(struct ip6_hdr); + if (plen < siz) { + printf("sanity fail: siz=%x, payloadlen=%x in %s:%d\n", + (unsigned int)siz, plen, __FILE__, __LINE__); + goto bad; + } + IP6_EXTHDR_CHECK(m, 0, off, /*nothing*/); + IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), /*nothing*/); + + ovbcopy((caddr_t)ip6, + (caddr_t)(mtod(m, u_char *) + siz), + sizeof(struct ip6_hdr)); + m->m_data += siz; + m->m_len -= siz; + m->m_pkthdr.len -= siz; + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_nxt = IPPROTO_ICMPV6; + plen -= siz; + } + + icmp6 = (struct icmp6_hdr *)(ip6 + 1); + type = icmp6->icmp6_type; /* keep type for statistics */ + code = icmp6->icmp6_code; /* ditto. */ + + t = ip6->ip6_dst; + /* + * ip6_input() drops a packet if its src is multicast. + * So, the src is never multicast. + */ + ip6->ip6_dst = ip6->ip6_src; + + /* XXX hack for link-local addresses */ + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = + htons(m->m_pkthdr.rcvif->if_index); + if (IN6_IS_ADDR_LINKLOCAL(&t)) + t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + +#ifdef COMPAT_RFC1885 + /* + * xxx guess MTU + * RFC 1885 requires that echo reply should be truncated if it + * does not fit in with (return) path MTU, but the description was + * removed in the new spec. + */ + if (icmp6_reflect_rt.ro_rt == 0 || + ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) { + if (icmp6_reflect_rt.ro_rt) { + RTFREE(icmp6_reflect_rt.ro_rt); + icmp6_reflect_rt.ro_rt = 0; + } + bzero(sin6, sizeof(*sin6)); + sin6->sin6_family = PF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_addr = ip6->ip6_dst; + + rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt, + RTF_PRCLONING); + } + + if (icmp6_reflect_rt.ro_rt == 0) + goto bad; + + if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST) + && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu) + mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu; + + if (mtu < m->m_pkthdr.len) { + plen -= (m->m_pkthdr.len - mtu); + m_adj(m, mtu - m->m_pkthdr.len); + } +#endif + /* + * If the incoming packet was addressed directly to us(i.e. unicast), + * use dst as the src for the reply. + */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) + if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) && + (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) { + src = &t; + break; + } + if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) { + /* + * This is the case if the dst is our link-local address + * and the sender is also ourseleves. + */ + src = &t; + } + + if (src == 0) + /* + * We have not multicast routing yet. So this case matches + * to our multicast, our anycast or not to our unicast. + * Select a source address which has the same scope. + */ + if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) + src = &IA6_SIN6(ia)->sin6_addr; + + if (src == 0) + goto bad; + + ip6->ip6_src = *src; + + ip6->ip6_flow = 0; + ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_nxt = IPPROTO_ICMPV6; + if (m->m_pkthdr.rcvif) { + /* XXX: This may not be the outgoing interface */ + ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; + } + + icmp6->icmp6_cksum = 0; + icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, + sizeof(struct ip6_hdr), plen); + + /* + * xxx option handling + */ + + m->m_flags &= ~(M_BCAST|M_MCAST); +#ifdef IPSEC + m->m_pkthdr.rcvif = NULL; +#endif /*IPSEC*/ + +#ifdef COMPAT_RFC1885 + ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif); +#else + ip6_output(m, NULL, NULL, 0, NULL, &outif); +#endif + if (outif) + icmp6_ifoutstat_inc(outif, type, code); + + return; + + bad: + m_freem(m); + return; +} + +void +icmp6_fasttimo() +{ + mld6_fasttimeo(); +} + +static const char * +icmp6_redirect_diag(src6, dst6, tgt6) + struct in6_addr *src6; + struct in6_addr *dst6; + struct in6_addr *tgt6; +{ + static char buf[1024]; + snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)", + ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6)); + return buf; +} + +void +icmp6_redirect_input(m, off) + register struct mbuf *m; + int off; +{ + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); + int icmp6len = ntohs(ip6->ip6_plen); + char *lladdr = NULL; + int lladdrlen = 0; + u_char *redirhdr = NULL; + int redirhdrlen = 0; + struct rtentry *rt = NULL; + int is_router; + int is_onlink; + struct in6_addr src6 = ip6->ip6_src; + struct in6_addr redtgt6 = nd_rd->nd_rd_target; + struct in6_addr reddst6 = nd_rd->nd_rd_dst; + union nd_opts ndopts; + + if (!m || !ifp) + return; + + /* XXX if we are router, we don't update route by icmp6 redirect */ + if (ip6_forwarding) + return; + if (!icmp6_rediraccept) + return; + + if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) + redtgt6.s6_addr16[1] = htons(ifp->if_index); + if (IN6_IS_ADDR_LINKLOCAL(&reddst6)) + reddst6.s6_addr16[1] = htons(ifp->if_index); + + /* validation */ + if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { + log(LOG_ERR, + "ICMP6 redirect sent from %s rejected; " + "must be from linklocal\n", ip6_sprintf(&src6)); + return; + } + if (ip6->ip6_hlim != 255) { + log(LOG_ERR, + "ICMP6 redirect sent from %s rejected; " + "hlim=%d (must be 255)\n", + ip6_sprintf(&src6), ip6->ip6_hlim); + return; + } + { + /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ + struct sockaddr_in6 sin6; + struct in6_addr *gw6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6)); + rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + if (rt) { + gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); + if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { + log(LOG_ERR, + "ICMP6 redirect rejected; " + "not equal to gw-for-src=%s (must be same): " + "%s\n", + ip6_sprintf(gw6), + icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + RTFREE(rt); + return; + } + } else { + log(LOG_ERR, + "ICMP6 redirect rejected; " + "no route found for redirect dst: %s\n", + icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + return; + } + RTFREE(rt); + rt = NULL; + } + if (IN6_IS_ADDR_MULTICAST(&reddst6)) { + log(LOG_ERR, + "ICMP6 redirect rejected; " + "redirect dst must be unicast: %s\n", + icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + return; + } + + is_router = is_onlink = 0; + if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) + is_router = 1; /* router case */ + if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) + is_onlink = 1; /* on-link destination case */ + if (!is_router && !is_onlink) { + log(LOG_ERR, + "ICMP6 redirect rejected; " + "neither router case nor onlink case: %s\n", + icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + return; + } + /* validation passed */ + + icmp6len -= sizeof(*nd_rd); + nd6_option_init(nd_rd + 1, icmp6len, &ndopts); + if (nd6_options(&ndopts) < 0) { + log(LOG_INFO, "icmp6_redirect_input: " + "invalid ND option, rejected: %s\n", + icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + return; + } + + if (ndopts.nd_opts_tgt_lladdr) { + lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); + lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; + } + + if (ndopts.nd_opts_rh) { + redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len; + redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */ + } + + if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { + log(LOG_INFO, + "icmp6_redirect_input: lladdrlen mismatch for %s " + "(if %d, icmp6 packet %d): %s\n", + ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2, + icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + } + + /* RFC 2461 8.3 */ + nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT, + is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER); + + if (!is_onlink) { /* better router case. perform rtredirect. */ + /* perform rtredirect */ + struct sockaddr_in6 sdst; + struct sockaddr_in6 sgw; + struct sockaddr_in6 ssrc; + + bzero(&sdst, sizeof(sdst)); + bzero(&sgw, sizeof(sgw)); + bzero(&ssrc, sizeof(ssrc)); + sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6; + sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len = + sizeof(struct sockaddr_in6); + bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); + bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); + bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); + rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw, + (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST, + (struct sockaddr *)&ssrc, + (struct rtentry **)NULL); + } + /* finally update cached route in each socket via pfctlinput */ + { + struct sockaddr_in6 sdst; + + bzero(&sdst, sizeof(sdst)); + sdst.sin6_family = AF_INET6; + sdst.sin6_len = sizeof(struct sockaddr_in6); + bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); + pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst); +#ifdef IPSEC + key_sa_routechange((struct sockaddr *)&sdst); +#endif + } +} + +void +icmp6_redirect_output(m0, rt) + struct mbuf *m0; + struct rtentry *rt; +{ + struct ifnet *ifp; /* my outgoing interface */ + struct in6_addr *ifp_ll6; + struct in6_addr *router_ll6; + struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */ + struct mbuf *m = NULL; /* newly allocated one */ + struct ip6_hdr *ip6; /* m as struct ip6_hdr */ + struct nd_redirect *nd_rd; + size_t maxlen; + u_char *p; + struct ifnet *outif = NULL; + + /* if we are not router, we don't send icmp6 redirect */ + if (!ip6_forwarding || ip6_accept_rtadv) + goto fail; + + /* sanity check */ + if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) + goto fail; + + /* + * Address check: + * the source address must identify a neighbor, and + * the destination address must not be a multicast address + * [RFC 2461, sec 8.2] + */ + sip6 = mtod(m0, struct ip6_hdr *); + if (nd6_is_addr_neighbor(&sip6->ip6_src, ifp) == 0) + goto fail; + if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) + goto fail; /* what should we do here? */ + + /* rate limit */ + if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) + goto fail; + + /* + * Since we are going to append up to 1280 bytes (= IPV6_MMTU), + * we almost always ask for an mbuf cluster for simplicity. + * (MHLEN < IPV6_MMTU is almost always true) + */ + MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (!m) + goto fail; + if (MHLEN < IPV6_MMTU) + MCLGET(m, M_DONTWAIT); + maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; + maxlen = min(IPV6_MMTU, maxlen); + /* just for safety */ + if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + goto fail; + + { + /* get ip6 linklocal address for ifp(my outgoing interface). */ + struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp); + if (ia == NULL) + goto fail; + ifp_ll6 = &ia->ia_addr.sin6_addr; + } + + /* get ip6 linklocal address for the router. */ + if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)rt->rt_gateway; + router_ll6 = &sin6->sin6_addr; + if (!IN6_IS_ADDR_LINKLOCAL(router_ll6)) + router_ll6 = (struct in6_addr *)NULL; + } else + router_ll6 = (struct in6_addr *)NULL; + + /* ip6 */ + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = 0; + ip6->ip6_vfc = IPV6_VERSION; + /* ip6->ip6_plen will be set later */ + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 255; + /* ip6->ip6_src must be linklocal addr for my outgoing if. */ + bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr)); + bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr)); + + /* ND Redirect */ + nd_rd = (struct nd_redirect *)(ip6 + 1); + nd_rd->nd_rd_type = ND_REDIRECT; + nd_rd->nd_rd_code = 0; + nd_rd->nd_rd_reserved = 0; + if (rt->rt_flags & RTF_GATEWAY) { + /* + * nd_rd->nd_rd_target must be a link-local address in + * better router cases. + */ + if (!router_ll6) + goto fail; + bcopy(router_ll6, &nd_rd->nd_rd_target, + sizeof(nd_rd->nd_rd_target)); + bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, + sizeof(nd_rd->nd_rd_dst)); + } else { + /* make sure redtgt == reddst */ + bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target, + sizeof(nd_rd->nd_rd_target)); + bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, + sizeof(nd_rd->nd_rd_dst)); + } + + p = (u_char *)(nd_rd + 1); + + if (!router_ll6) + goto nolladdropt; + + { + /* target lladdr option */ + struct rtentry *rt_router = NULL; + int len; + struct sockaddr_dl *sdl; + struct nd_opt_hdr *nd_opt; + char *lladdr; + + rt_router = nd6_lookup(router_ll6, 0, ifp); + if (!rt_router) + goto nolladdropt; + if (!(rt_router->rt_flags & RTF_GATEWAY) + && (rt_router->rt_flags & RTF_LLINFO) + && (rt_router->rt_gateway->sa_family == AF_LINK) + && (sdl = (struct sockaddr_dl *)rt_router->rt_gateway)) { + nd_opt = (struct nd_opt_hdr *)p; + nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; + len = 2 + ifp->if_addrlen; + len = (len + 7) & ~7; /*round by 8*/ + nd_opt->nd_opt_len = len >> 3; + p += len; + lladdr = (char *)(nd_opt + 1); + bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); + } + } +nolladdropt:; + + m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; + + /* just to be safe */ + if (m0->m_flags & M_DECRYPTED) + goto noredhdropt; + + { + /* redirected header option */ + int len; + struct nd_opt_rd_hdr *nd_opt_rh; + + /* + * compute the maximum size for icmp6 redirect header option. + * XXX room for auth header? + */ + len = maxlen - (p - (u_char *)ip6); + len &= ~7; + + /* This is just for simplicity. */ + if (m0->m_pkthdr.len != m0->m_len) { + if (m0->m_next) { + m_freem(m0->m_next); + m0->m_next = NULL; + } + m0->m_pkthdr.len = m0->m_len; + } + + /* + * Redirected header option spec (RFC2461 4.6.3) talks nothing + * about padding/truncate rule for the original IP packet. + * From the discussion on IPv6imp in Feb 1999, the consensus was: + * - "attach as much as possible" is the goal + * - pad if not aligned (original size can be guessed by original + * ip6 header) + * Following code adds the padding if it is simple enough, + * and truncates if not. + */ + if (m0->m_next || m0->m_pkthdr.len != m0->m_len) + panic("assumption failed in %s:%d\n", __FILE__, __LINE__); + + if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) { + /* not enough room, truncate */ + m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); + } else { + /* enough room, pad or truncate */ + size_t extra; + + extra = m0->m_pkthdr.len % 8; + if (extra) { + /* pad if easy enough, truncate if not */ + if (8 - extra <= M_TRAILINGSPACE(m0)) { + /* pad */ + m0->m_len += (8 - extra); + m0->m_pkthdr.len += (8 - extra); + } else { + /* truncate */ + m0->m_pkthdr.len -= extra; + m0->m_len -= extra; + } + } + len = m0->m_pkthdr.len + sizeof(*nd_opt_rh); + m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); + } + + nd_opt_rh = (struct nd_opt_rd_hdr *)p; + bzero(nd_opt_rh, sizeof(*nd_opt_rh)); + nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER; + nd_opt_rh->nd_opt_rh_len = len >> 3; + p += sizeof(*nd_opt_rh); + m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; + + /* connect m0 to m */ + m->m_next = m0; + m->m_pkthdr.len = m->m_len + m0->m_len; + } +noredhdropt:; + + if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src)) + sip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst)) + sip6->ip6_dst.s6_addr16[1] = 0; + if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target)) + nd_rd->nd_rd_target.s6_addr16[1] = 0; + if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst)) + nd_rd->nd_rd_dst.s6_addr16[1] = 0; + + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); + + nd_rd->nd_rd_cksum = 0; + nd_rd->nd_rd_cksum + = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); + + /* send the packet to outside... */ +#ifdef IPSEC + m->m_pkthdr.rcvif = NULL; +#endif /*IPSEC*/ + ip6_output(m, NULL, NULL, 0, NULL, &outif); + if (outif) { + icmp6_ifstat_inc(outif, ifs6_out_msg); + icmp6_ifstat_inc(outif, ifs6_out_redirect); + } + icmp6stat.icp6s_outhist[ND_REDIRECT]++; + + return; + +fail: + if (m) + m_freem(m); + if (m0) + m_freem(m0); +} + +/* + * ICMPv6 socket option processing. + */ +int +icmp6_ctloutput(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + int error = 0; + int optlen; + register struct inpcb *inp = sotoinpcb(so); + int level, op, optname; + + if (sopt) { + level = sopt->sopt_level; + op = sopt->sopt_dir; + optname = sopt->sopt_name; + optlen = sopt->sopt_valsize; + } else + level = op = optname = optlen = 0; + if (level != IPPROTO_ICMPV6) { + return EINVAL; + } + + switch(op) { + case PRCO_SETOPT: + switch (optname) { + case ICMP6_FILTER: + { + struct icmp6_filter *p; + + if (optlen != sizeof(*p)) { + error = EMSGSIZE; + break; + } + if (inp->in6p_icmp6filt == NULL) { + error = EINVAL; + break; + } + error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen, + optlen); + break; + } + + default: + error = ENOPROTOOPT; + break; + } + break; + + case PRCO_GETOPT: + switch (optname) { + case ICMP6_FILTER: + { + if (inp->in6p_icmp6filt == NULL) { + error = EINVAL; + break; + } + error = sooptcopyout(sopt, inp->in6p_icmp6filt, + sizeof(struct icmp6_filter)); + break; + } + + default: + error = ENOPROTOOPT; + break; + } + break; + } + + return(error); +} + +/* + * Perform rate limit check. + * Returns 0 if it is okay to send the icmp6 packet. + * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate + * limitation. + * + * XXX per-destination/type check necessary? + */ +static int +icmp6_ratelimit(dst, type, code) + const struct in6_addr *dst; /* not used at this moment */ + const int type; /* not used at this moment */ + const int code; /* not used at this moment */ +{ + struct timeval tp; + long sec_diff, usec_diff; + + /* If we are not doing rate limitation, it is always okay to send */ + if (!icmp6errratelim) + return 0; + + microtime(&tp); + tp.tv_sec = time_second; + if (tp.tv_sec < icmp6_nextsend.tv_sec + || (tp.tv_sec == icmp6_nextsend.tv_sec + && tp.tv_usec < icmp6_nextsend.tv_usec)) { + /* The packet is subject to rate limit */ + return 1; + } + sec_diff = icmp6errratelim / 1000000; + usec_diff = icmp6errratelim % 1000000; + icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff; + if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) { + icmp6_nextsend.tv_sec++; + icmp6_nextsend.tv_usec -= 1000000; + } + + /* it is okay to send this */ + return 0; +} diff --git a/sys/netinet6/icmp6.h b/sys/netinet6/icmp6.h new file mode 100644 index 0000000..8fd63ff --- /dev/null +++ b/sys/netinet6/icmp6.h @@ -0,0 +1,602 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET6_ICMPV6_H_ +#define _NETINET6_ICMPV6_H_ + +#define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr) + - sizeof(struct icmp6_hdr) */ + +struct icmp6_hdr { + u_int8_t icmp6_type; /* type field */ + u_int8_t icmp6_code; /* code field */ + u_int16_t icmp6_cksum; /* checksum field */ + union { + u_int32_t icmp6_un_data32[1]; /* type-specific field */ + u_int16_t icmp6_un_data16[2]; /* type-specific field */ + u_int8_t icmp6_un_data8[4]; /* type-specific field */ + } icmp6_dataun; +}; + +#define icmp6_data32 icmp6_dataun.icmp6_un_data32 +#define icmp6_data16 icmp6_dataun.icmp6_un_data16 +#define icmp6_data8 icmp6_dataun.icmp6_un_data8 +#define icmp6_pptr icmp6_data32[0] /* parameter prob */ +#define icmp6_mtu icmp6_data32[0] /* packet too big */ +#define icmp6_id icmp6_data16[0] /* echo request/reply */ +#define icmp6_seq icmp6_data16[1] /* echo request/reply */ +#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */ + +#define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */ +#define ICMP6_PACKET_TOO_BIG 2 /* packet too big */ +#define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */ +#define ICMP6_PARAM_PROB 4 /* ip6 header bad */ + +#define ICMP6_ECHO_REQUEST 128 /* echo service */ +#define ICMP6_ECHO_REPLY 129 /* echo reply */ +#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */ +#define MLD6_LISTENER_QUERY 130 /* multicast listener query */ +#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */ +#define MLD6_LISTENER_REPORT 131 /* multicast listener report */ +#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */ +#define MLD6_LISTENER_DONE 132 /* multicast listener done */ + +#define ND_ROUTER_SOLICIT 133 /* router solicitation */ +#define ND_ROUTER_ADVERT 134 /* router advertisment */ +#define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */ +#define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisment */ +#define ND_REDIRECT 137 /* redirect */ + +#define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */ + +#define ICMP6_WRUREQUEST 139 /* who are you request */ +#define ICMP6_WRUREPLY 140 /* who are you reply */ +#define ICMP6_FQDN_QUERY 139 /* FQDN query */ +#define ICMP6_FQDN_REPLY 140 /* FQDN reply */ +#define ICMP6_NI_QUERY 139 /* node information request */ +#define ICMP6_NI_REPLY 140 /* node information reply */ + +/* The definitions below are experimental. TBA */ +#define MLD6_MTRACE_RESP 141 /* mtrace response(to sender) */ +#define MLD6_MTRACE 142 /* mtrace messages */ + +#define ICMP6_MAXTYPE 142 + +#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */ +#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */ +#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */ +#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */ +#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */ +#define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */ + +#define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */ +#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */ + +#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */ +#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */ +#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */ + +#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */ + +#define ICMP6_NI_SUCESS 0 /* node information successful reply */ +#define ICMP6_NI_REFUSED 1 /* node information request is refused */ +#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ + +#define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */ +#define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */ +#define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */ + +/* Used in kernel only */ +#define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */ +#define ND_REDIRECT_ROUTER 1 /* redirect to a better router */ + +/* + * Multicast Listener Discovery + */ +struct mld6_hdr { + struct icmp6_hdr mld6_hdr; + struct in6_addr mld6_addr; /* multicast address */ +}; + +#define mld6_type mld6_hdr.icmp6_type +#define mld6_code mld6_hdr.icmp6_code +#define mld6_cksum mld6_hdr.icmp6_cksum +#define mld6_maxdelay mld6_hdr.icmp6_data16[0] +#define mld6_reserved mld6_hdr.icmp6_data16[1] + +/* + * Neighbor Discovery + */ + +struct nd_router_solicit { /* router solicitation */ + struct icmp6_hdr nd_rs_hdr; + /* could be followed by options */ +}; + +#define nd_rs_type nd_rs_hdr.icmp6_type +#define nd_rs_code nd_rs_hdr.icmp6_code +#define nd_rs_cksum nd_rs_hdr.icmp6_cksum +#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0] + +struct nd_router_advert { /* router advertisement */ + struct icmp6_hdr nd_ra_hdr; + u_int32_t nd_ra_reachable; /* reachable time */ + u_int32_t nd_ra_retransmit; /* retransmit timer */ + /* could be followed by options */ +}; + +#define nd_ra_type nd_ra_hdr.icmp6_type +#define nd_ra_code nd_ra_hdr.icmp6_code +#define nd_ra_cksum nd_ra_hdr.icmp6_cksum +#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0] +#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1] +#define ND_RA_FLAG_MANAGED 0x80 +#define ND_RA_FLAG_OTHER 0x40 +#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1] + +struct nd_neighbor_solicit { /* neighbor solicitation */ + struct icmp6_hdr nd_ns_hdr; + struct in6_addr nd_ns_target; /*target address */ + /* could be followed by options */ +}; + +#define nd_ns_type nd_ns_hdr.icmp6_type +#define nd_ns_code nd_ns_hdr.icmp6_code +#define nd_ns_cksum nd_ns_hdr.icmp6_cksum +#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] + +struct nd_neighbor_advert { /* neighbor advertisement */ + struct icmp6_hdr nd_na_hdr; + struct in6_addr nd_na_target; /* target address */ + /* could be followed by options */ +}; + +#define nd_na_type nd_na_hdr.icmp6_type +#define nd_na_code nd_na_hdr.icmp6_code +#define nd_na_cksum nd_na_hdr.icmp6_cksum +#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0] +#if BYTE_ORDER == BIG_ENDIAN +#define ND_NA_FLAG_ROUTER 0x80000000 +#define ND_NA_FLAG_SOLICITED 0x40000000 +#define ND_NA_FLAG_OVERRIDE 0x20000000 +#elif BYTE_ORDER == LITTLE_ENDIAN +#define ND_NA_FLAG_ROUTER 0x80 +#define ND_NA_FLAG_SOLICITED 0x40 +#define ND_NA_FLAG_OVERRIDE 0x20 +#endif + +struct nd_redirect { /* redirect */ + struct icmp6_hdr nd_rd_hdr; + struct in6_addr nd_rd_target; /* target address */ + struct in6_addr nd_rd_dst; /* destination address */ + /* could be followed by options */ +}; + +#define nd_rd_type nd_rd_hdr.icmp6_type +#define nd_rd_code nd_rd_hdr.icmp6_code +#define nd_rd_cksum nd_rd_hdr.icmp6_cksum +#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] + +struct nd_opt_hdr { /* Neighbor discovery option header */ + u_int8_t nd_opt_type; + u_int8_t nd_opt_len; + /* followed by option specific data*/ +}; + +#define ND_OPT_SOURCE_LINKADDR 1 +#define ND_OPT_TARGET_LINKADDR 2 +#define ND_OPT_PREFIX_INFORMATION 3 +#define ND_OPT_REDIRECTED_HEADER 4 +#define ND_OPT_MTU 5 + +struct nd_opt_prefix_info { /* prefix information */ + u_int8_t nd_opt_pi_type; + u_int8_t nd_opt_pi_len; + u_int8_t nd_opt_pi_prefix_len; + u_int8_t nd_opt_pi_flags_reserved; + u_int32_t nd_opt_pi_valid_time; + u_int32_t nd_opt_pi_preferred_time; + u_int32_t nd_opt_pi_reserved2; + struct in6_addr nd_opt_pi_prefix; +}; + +#define ND_OPT_PI_FLAG_ONLINK 0x80 +#define ND_OPT_PI_FLAG_AUTO 0x40 + +struct nd_opt_rd_hdr { /* redirected header */ + u_int8_t nd_opt_rh_type; + u_int8_t nd_opt_rh_len; + u_int16_t nd_opt_rh_reserved1; + u_int32_t nd_opt_rh_reserved2; + /* followed by IP header and data */ +}; + +struct nd_opt_mtu { /* MTU option */ + u_int8_t nd_opt_mtu_type; + u_int8_t nd_opt_mtu_len; + u_int16_t nd_opt_mtu_reserved; + u_int32_t nd_opt_mtu_mtu; +}; + +/* + * icmp6 namelookup + */ + +struct icmp6_namelookup { + struct icmp6_hdr icmp6_nl_hdr; + u_int64_t icmp6_nl_nonce; + u_int32_t icmp6_nl_ttl; + /* could be followed by options */ +}; + +/* + * icmp6 node information + */ +struct icmp6_nodeinfo { + struct icmp6_hdr icmp6_ni_hdr; + u_int64_t icmp6_ni_nonce; + /* could be followed by reply data */ +}; + +#define ni_type icmp6_ni_hdr.icmp6_type +#define ni_code icmp6_ni_hdr.icmp6_code +#define ni_cksum icmp6_ni_hdr.icmp6_cksum +#define ni_qtype icmp6_ni_hdr.icmp6_data16[0] +#define ni_flags icmp6_ni_hdr.icmp6_data16[1] + + +#define NI_QTYPE_NOOP 0 /* NOOP */ +#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ +#define NI_QTYPE_FQDN 2 /* FQDN */ +#define NI_QTYPE_NODEADDR 3 /* Node Addresses. XXX: spec says 2, but it may be a typo... */ + +#if BYTE_ORDER == BIG_ENDIAN +#define NI_SUPTYPE_FLAG_COMPRESS 0x1 +#define NI_FQDN_FLAG_VALIDTTL 0x1 +#define NI_NODEADDR_FLAG_LINKLOCAL 0x1 +#define NI_NODEADDR_FLAG_SITELOCAL 0x2 +#define NI_NODEADDR_FLAG_GLOBAL 0x4 +#define NI_NODEADDR_FLAG_ALL 0x8 +#define NI_NODEADDR_FLAG_TRUNCATE 0x10 +#define NI_NODEADDR_FLAG_ANYCAST 0x20 /* just experimental. not in spec */ +#elif BYTE_ORDER == LITTLE_ENDIAN +#define NI_SUPTYPE_FLAG_COMPRESS 0x0100 +#define NI_FQDN_FLAG_VALIDTTL 0x0100 +#define NI_NODEADDR_FLAG_LINKLOCAL 0x0100 +#define NI_NODEADDR_FLAG_SITELOCAL 0x0200 +#define NI_NODEADDR_FLAG_GLOBAL 0x0400 +#define NI_NODEADDR_FLAG_ALL 0x0800 +#define NI_NODEADDR_FLAG_TRUNCATE 0x1000 +#define NI_NODEADDR_FLAG_ANYCAST 0x2000 /* just experimental. not in spec */ +#endif + +struct ni_reply_fqdn { + u_int32_t ni_fqdn_ttl; /* TTL */ + u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */ + u_int8_t ni_fqdn_name[3]; /* XXX: alignment */ +}; + +/* + * Router Renumbering. as router-renum-08.txt + */ +struct icmp6_router_renum { /* router renumbering header */ + struct icmp6_hdr rr_hdr; + u_int8_t rr_segnum; + u_int8_t rr_flags; + u_int16_t rr_maxdelay; + u_int32_t rr_reserved; +}; +#define ICMP6_RR_FLAGS_SEGNUM 0x80 +#define ICMP6_RR_FLAGS_TEST 0x40 +#define ICMP6_RR_FLAGS_REQRESULT 0x20 +#define ICMP6_RR_FLAGS_FORCEAPPLY 0x10 +#define ICMP6_RR_FLAGS_SPECSITE 0x08 +#define ICMP6_RR_FLAGS_PREVDONE 0x04 + +#define rr_type rr_hdr.icmp6_type +#define rr_code rr_hdr.icmp6_code +#define rr_cksum rr_hdr.icmp6_cksum +#define rr_seqnum rr_hdr.icmp6_data32[0] + +struct rr_pco_match { /* match prefix part */ + u_int8_t rpm_code; + u_int8_t rpm_len; + u_int8_t rpm_ordinal; + u_int8_t rpm_matchlen; + u_int8_t rpm_minlen; + u_int8_t rpm_maxlen; + u_int16_t rpm_reserved; + struct in6_addr rpm_prefix; +}; + +#define RPM_PCO_ADD 1 +#define RPM_PCO_CHANGE 2 +#define RPM_PCO_SETGLOBAL 3 + +struct rr_pco_use { /* use prefix part */ + u_int8_t rpu_uselen; + u_int8_t rpu_keeplen; + u_int8_t rpu_ramask; + u_int8_t rpu_raflags; + u_int32_t rpu_vltime; + u_int32_t rpu_pltime; + u_int32_t rpu_flags; + struct in6_addr rpu_prefix; +}; +#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x20 +#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x10 + +#if BYTE_ORDER == BIG_ENDIAN +#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80000000 +#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40000000 +#elif BYTE_ORDER == LITTLE_ENDIAN +#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80 +#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40 +#endif + +struct rr_result { /* router renumbering result message */ + u_int16_t rrr_flags; + u_int8_t rrr_ordinal; + u_int8_t rrr_matchedlen; + u_int32_t rrr_ifid; + struct in6_addr rrr_prefix; +}; +#if BYTE_ORDER == BIG_ENDIAN +#define ICMP6_RR_RESULT_FLAGS_OOB 0x0002 +#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001 +#elif BYTE_ORDER == LITTLE_ENDIAN +#define ICMP6_RR_RESULT_FLAGS_OOB 0x02 +#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x01 +#endif + +/* + * icmp6 filter structures. + */ + +struct icmp6_filter { + u_int32_t icmp6_filter[8]; +}; + +#ifdef _KERNEL +#define ICMP6_FILTER_SETPASSALL(filterp) \ + { \ + int i; u_char *p; \ + p = (u_char *)filterp; \ + for (i = 0; i < sizeof(struct icmp6_filter); i++) \ + p[i] = 0xff; \ + } +#define ICMP6_FILTER_SETBLOCKALL(filterp) \ + bzero(filterp, sizeof(struct icmp6_filter)) +#else /* _KERNEL */ +#define ICMP6_FILTER_SETPASSALL(filterp) \ + memset(filterp, 0xff, sizeof(struct icmp6_filter)) +#define ICMP6_FILTER_SETBLOCKALL(filterp) \ + memset(filterp, 0x00, sizeof(struct icmp6_filter)) +#endif /* _KERNEL */ + +#define ICMP6_FILTER_SETPASS(type, filterp) \ + (((filterp)->icmp6_filter[(type) >> 5]) |= (1 << ((type) & 31))) +#define ICMP6_FILTER_SETBLOCK(type, filterp) \ + (((filterp)->icmp6_filter[(type) >> 5]) &= ~(1 << ((type) & 31))) +#define ICMP6_FILTER_WILLPASS(type, filterp) \ + ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) != 0) +#define ICMP6_FILTER_WILLBLOCK(type, filterp) \ + ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) == 0) + +/* + * Variables related to this implementation + * of the internet control message protocol version 6. + */ +struct icmp6stat { +/* statistics related to icmp6 packets generated */ + u_long icp6s_error; /* # of calls to icmp6_error */ + u_long icp6s_canterror; /* no error 'cuz old was icmp */ + u_long icp6s_toofreq; /* no error 'cuz rate limitation */ + u_long icp6s_outhist[256]; +/* statistics related to input messages proccesed */ + u_long icp6s_badcode; /* icmp6_code out of range */ + u_long icp6s_tooshort; /* packet < sizeof(struct icmp6_hdr) */ + u_long icp6s_checksum; /* bad checksum */ + u_long icp6s_badlen; /* calculated bound mismatch */ + u_long icp6s_reflect; /* number of responses */ + u_long icp6s_inhist[256]; +}; + +/* + * Names for ICMP sysctl objects + */ +#define ICMPV6CTL_STATS 1 +#define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */ +#define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */ +#define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */ +#define ICMPV6CTL_ND6_PRUNE 6 +#define ICMPV6CTL_ND6_DELAY 8 +#define ICMPV6CTL_ND6_UMAXTRIES 9 +#define ICMPV6CTL_ND6_MMAXTRIES 10 +#define ICMPV6CTL_ND6_USELOOPBACK 11 +#define ICMPV6CTL_ND6_PROXYALL 12 +#define ICMPV6CTL_MAXID 13 + +#define ICMPV6CTL_NAMES { \ + { 0, 0 }, \ + { 0, 0 }, \ + { "rediraccept", CTLTYPE_INT }, \ + { "redirtimeout", CTLTYPE_INT }, \ + { 0, 0 }, \ + { "errratelimit", CTLTYPE_INT }, \ + { "nd6_prune", CTLTYPE_INT }, \ + { 0, 0 }, \ + { "nd6_delay", CTLTYPE_INT }, \ + { "nd6_umaxtries", CTLTYPE_INT }, \ + { "nd6_mmaxtries", CTLTYPE_INT }, \ + { "nd6_useloopback", CTLTYPE_INT }, \ + { "nd6_proxyall", CTLTYPE_INT }, \ +} + +#define ICMPV6CTL_VARS { \ + 0, \ + 0, \ + &icmp6_rediraccept, \ + &icmp6_redirtimeout, \ + 0, \ + 0, \ + &icmp6errratelim, \ + &nd6_prune, \ + 0, \ + &nd6_delay, \ + &nd6_umaxtries, \ + &nd6_mmaxtries, \ + &nd6_useloopback, \ + &nd6_proxyall, \ +} + +#define RTF_PROBEMTU RTF_PROTO1 + +#ifdef _KERNEL +# ifdef __STDC__ +struct rtentry; +struct rttimer; +struct in6_multi; +# endif +void icmp6_init __P((void)); +void icmp6_paramerror __P((struct mbuf *, int)); +void icmp6_error __P((struct mbuf *, int, int, int)); +int icmp6_input __P((struct mbuf **, int *, int)); +void icmp6_fasttimo __P((void)); +void icmp6_reflect __P((struct mbuf *, size_t)); +void icmp6_prepare __P((struct mbuf *)); +void icmp6_redirect_input __P((struct mbuf *, int)); +void icmp6_redirect_output __P((struct mbuf *, struct rtentry *)); + +/* XXX: is this the right place for these macros? */ +#define icmp6_ifstat_inc(ifp, tag) \ +do { \ + if ((ifp) && (ifp)->if_index <= if_index \ + && (ifp)->if_index < icmp6_ifstatmax \ + && icmp6_ifstat && icmp6_ifstat[(ifp)->if_index]) { \ + icmp6_ifstat[(ifp)->if_index]->tag++; \ + } \ +} while (0) + +#define icmp6_ifoutstat_inc(ifp, type, code) \ +do { \ + icmp6_ifstat_inc(ifp, ifs6_out_msg); \ + if (type < ICMP6_INFOMSG_MASK) \ + icmp6_ifstat_inc(ifp, ifs6_out_error); \ + switch(type) { \ + case ICMP6_DST_UNREACH: \ + icmp6_ifstat_inc(ifp, ifs6_out_dstunreach); \ + if (code == ICMP6_DST_UNREACH_ADMIN) \ + icmp6_ifstat_inc(ifp, ifs6_out_adminprohib); \ + break; \ + case ICMP6_PACKET_TOO_BIG: \ + icmp6_ifstat_inc(ifp, ifs6_out_pkttoobig); \ + break; \ + case ICMP6_TIME_EXCEEDED: \ + icmp6_ifstat_inc(ifp, ifs6_out_timeexceed); \ + break; \ + case ICMP6_PARAM_PROB: \ + icmp6_ifstat_inc(ifp, ifs6_out_paramprob); \ + break; \ + case ICMP6_ECHO_REQUEST: \ + icmp6_ifstat_inc(ifp, ifs6_out_echo); \ + break; \ + case ICMP6_ECHO_REPLY: \ + icmp6_ifstat_inc(ifp, ifs6_out_echoreply); \ + break; \ + case MLD6_LISTENER_QUERY: \ + icmp6_ifstat_inc(ifp, ifs6_out_mldquery); \ + break; \ + case MLD6_LISTENER_REPORT: \ + icmp6_ifstat_inc(ifp, ifs6_out_mldreport); \ + break; \ + case MLD6_LISTENER_DONE: \ + icmp6_ifstat_inc(ifp, ifs6_out_mlddone); \ + break; \ + case ND_ROUTER_SOLICIT: \ + icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); \ + break; \ + case ND_ROUTER_ADVERT: \ + icmp6_ifstat_inc(ifp, ifs6_out_routeradvert); \ + break; \ + case ND_NEIGHBOR_SOLICIT: \ + icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); \ + break; \ + case ND_NEIGHBOR_ADVERT: \ + icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); \ + break; \ + case ND_REDIRECT: \ + icmp6_ifstat_inc(ifp, ifs6_out_redirect); \ + break; \ + } \ +} while (0) + +extern int icmp6_rediraccept; /* accept/process redirects */ +extern int icmp6_redirtimeout; /* cache time for redirect routes */ +#endif /* _KERNEL */ + +#endif /* not _NETINET6_ICMPV6_H_ */ + diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c new file mode 100644 index 0000000..291b672 --- /dev/null +++ b/sys/netinet6/in6.c @@ -0,0 +1,1879 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in.c 8.2 (Berkeley) 11/15/93 + */ + +#include "opt_inet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +/* #include "gif.h" */ +#if NGIF > 0 +#include +#endif +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address"); + +/* + * Definitions of some costant IP6 addresses. + */ +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; +const struct in6_addr in6addr_nodelocal_allnodes = + IN6ADDR_NODELOCAL_ALLNODES_INIT; +const struct in6_addr in6addr_linklocal_allnodes = + IN6ADDR_LINKLOCAL_ALLNODES_INIT; +const struct in6_addr in6addr_linklocal_allrouters = + IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; + +const struct in6_addr in6mask0 = IN6MASK0; +const struct in6_addr in6mask32 = IN6MASK32; +const struct in6_addr in6mask64 = IN6MASK64; +const struct in6_addr in6mask96 = IN6MASK96; +const struct in6_addr in6mask128 = IN6MASK128; + +static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, + struct ifnet *, struct proc *)); +struct in6_multihead in6_multihead; /* XXX BSS initialization */ + +/* + * Determine whether an IP6 address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + */ +int +in6_canforward(src, dst) + struct in6_addr *src, *dst; +{ + if (IN6_IS_ADDR_LINKLOCAL(src) || + IN6_IS_ADDR_LINKLOCAL(dst) || + IN6_IS_ADDR_MULTICAST(dst)) + return(0); + return(1); +} + +/* + * Check if the loopback entry will be automatically generated. + * if 0 returned, will not be automatically generated. + * if 1 returned, will be automatically generated. + */ +static int +in6_is_ifloop_auto(struct ifaddr *ifa) +{ +#define SIN6(s) ((struct sockaddr_in6 *)s) + /* + * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT), + * or netmask is all0 or all1, then cloning will not happen, + * then we can't rely on its loopback entry generation. + */ + if ((ifa->ifa_flags & RTF_CLONING) == 0 || + (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || + (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6) + && + IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr, + &in6mask128)) || + ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0) + return 0; + else + return 1; +#undef SIN6 +} + +/* + * Subroutine for in6_ifaddloop() and in6_ifremloop(). + * This routine does actual work. + */ +static void +in6_ifloop_request(int cmd, struct ifaddr *ifa) +{ + struct sockaddr_in6 lo_sa; + struct sockaddr_in6 all1_sa; + struct rtentry *nrt = NULL; + + bzero(&lo_sa, sizeof(lo_sa)); + bzero(&all1_sa, sizeof(all1_sa)); + lo_sa.sin6_family = AF_INET6; + lo_sa.sin6_len = sizeof(struct sockaddr_in6); + all1_sa = lo_sa; + lo_sa.sin6_addr = in6addr_loopback; + all1_sa.sin6_addr = in6mask128; + + /* So we add or remove static loopback entry, here. */ + rtrequest(cmd, ifa->ifa_addr, + (struct sockaddr *)&lo_sa, + (struct sockaddr *)&all1_sa, + RTF_UP|RTF_HOST, &nrt); + + /* + * Make sure rt_ifa be equal to IFA, the second argument of the + * function. + * We need this because when we refer rt_ifa->ia6_flags in ip6_input, + * we assume that the rt_ifa points to the address instead of the + * loopback address. + */ + if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) { + nrt->rt_ifa->ifa_refcnt--; + ifa->ifa_refcnt++; + nrt->rt_ifa = ifa; + } + if (nrt) + nrt->rt_refcnt--; +} + +/* + * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). + * Because, KAME needs loopback rtentry for ownaddr check in + * ip6_input(). + */ +static void +in6_ifaddloop(struct ifaddr *ifa) +{ + if (!in6_is_ifloop_auto(ifa)) { + struct rtentry *rt; + + /* If there is no loopback entry, allocate one. */ + rt = rtalloc1(ifa->ifa_addr, 0, 0); + if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) + in6_ifloop_request(RTM_ADD, ifa); + if (rt) + rt->rt_refcnt--; + } +} + +/* + * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(), + * if it exists. + */ +static void +in6_ifremloop(struct ifaddr *ifa) +{ + if (!in6_is_ifloop_auto(ifa)) { + struct in6_ifaddr *ia; + int ia_count = 0; + + /* If only one ifa for the loopback entry, delete it. */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), + &ia->ia_addr.sin6_addr)) { + ia_count++; + if (ia_count > 1) + break; + } + } + if (ia_count == 1) + in6_ifloop_request(RTM_DELETE, ifa); + } +} + +/* + * Subroutine for in6_ifaddproxy() and in6_ifremproxy(). + * This routine does actual work. + * call in6_addmulti() when cmd == 1. + * call in6_delmulti() when cmd == 2. + */ +static int +in6_ifproxy_request(int cmd, struct in6_ifaddr *ia) +{ + int error = 0; + + /* + * If we have an IPv6 dstaddr on adding p2p interface, + * join dstaddr's solicited multicast on necessary interface. + */ + if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) && + ia->ia_dstaddr.sin6_family == AF_INET6 && + !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { + struct in6_ifaddr *ia_lan; + + /* + * TODO: Join only on some specified interfaces by some + * configuration. + * Unsolicited Neighbor Advertisements will be also necessary. + * + * Now, join on interfaces which meets following. + * -IFF_BROADCAST and IFF_MULTICAST + * (NBMA is out of scope) + * -the prefix value is same as p2p dstaddr + */ + for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) { + struct in6_addr llsol; + + if ((ia_lan->ia_ifp->if_flags & + (IFF_BROADCAST|IFF_MULTICAST)) != + (IFF_BROADCAST|IFF_MULTICAST)) + continue; + if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia), + IA6_IN6(ia_lan), + IA6_MASKIN6(ia_lan))) + continue; + if (ia_lan->ia_ifp == ia->ia_ifp) + continue; + + /* init llsol */ + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = + ia->ia_dstaddr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + + if (cmd == 1) + (void)in6_addmulti(&llsol, + ia_lan->ia_ifp, + &error); + else if (cmd == 2) { + struct in6_multi *in6m; + + IN6_LOOKUP_MULTI(llsol, + ia_lan->ia_ifp, + in6m); + if (in6m) + in6_delmulti(in6m); + } + } + } + return error; +} + +static int +in6_ifaddproxy(struct in6_ifaddr *ia) +{ + return(in6_ifproxy_request(1, ia)); +} + +static void +in6_ifremproxy(struct in6_ifaddr *ia) +{ + in6_ifproxy_request(2, ia); +} + +int +in6_ifindex2scopeid(idx) + int idx; +{ + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_in6 *sin6; + + if (idx < 0 || if_index < idx) + return -1; + ifp = ifindex2ifnet[idx]; + + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) + return sin6->sin6_scope_id & 0xffff; + } + + return -1; +} + +int +in6_mask2len(mask) + struct in6_addr *mask; +{ + int x, y; + + for (x = 0; x < sizeof(*mask); x++) { + if (mask->s6_addr8[x] != 0xff) + break; + } + y = 0; + if (x < sizeof(*mask)) { + for (y = 0; y < 8; y++) { + if ((mask->s6_addr8[x] & (0x80 >> y)) == 0) + break; + } + } + return x * 8 + y; +} + +void +in6_len2mask(mask, len) + struct in6_addr *mask; + int len; +{ + int i; + + bzero(mask, sizeof(*mask)); + for (i = 0; i < len / 8; i++) + mask->s6_addr8[i] = 0xff; + if (len % 8) + mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff; +} + +int in6_interfaces; /* number of external internet interfaces */ + +#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) +#define ia62ifa(ia6) ((struct ifaddr *)(ia6)) + +int +in6_control(so, cmd, data, ifp, p) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; + struct proc *p; +{ + struct in6_ifreq *ifr = (struct in6_ifreq *)data; + struct in6_ifaddr *ia, *oia; + struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; + struct sockaddr_in6 oldaddr, net; + int error = 0, hostIsNew, prefixIsNew; + int privileged; + + privileged = 0; + if (p && !suser(p)) + privileged++; + + /* + * xxx should prevent processes for link-local addresses? + */ +#if NGIF > 0 + if (ifp && ifp->if_type == IFT_GIF) { + switch (cmd) { + case SIOCSIFPHYADDR_IN6: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGIFPSRCADDR_IN6: + case SIOCGIFPDSTADDR_IN6: + return gif_ioctl(ifp, cmd, data); + } + } +#endif + + if (ifp == 0) + return(EOPNOTSUPP); + + switch (cmd) { + case SIOCSNDFLUSH_IN6: + case SIOCSPFXFLUSH_IN6: + case SIOCSRTRFLUSH_IN6: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGIFINFO_IN6: + case SIOCGDRLST_IN6: + case SIOCGPRLST_IN6: + case SIOCGNBRINFO_IN6: + return(nd6_ioctl(cmd, data, ifp)); + } + + switch (cmd) { + case SIOCSIFPREFIX_IN6: + case SIOCDIFPREFIX_IN6: + case SIOCAIFPREFIX_IN6: + case SIOCCIFPREFIX_IN6: + case SIOCSGIFPREFIX_IN6: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGIFPREFIX_IN6: + return(in6_prefix_ioctl(so, cmd, data, ifp)); + } + + switch (cmd) { + case SIOCALIFADDR: + case SIOCDLIFADDR: + if (!privileged) + return(EPERM); + /*fall through*/ + case SIOCGLIFADDR: + return in6_lifaddr_ioctl(so, cmd, data, ifp, p); + } + + /* + * Find address for this interface, if it exists. + */ + { + + struct sockaddr_in6 *sa6 = + (struct sockaddr_in6 *)&ifra->ifra_addr; + + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { + if (sa6->sin6_addr.s6_addr16[1] == 0) { + /* interface ID is not embedded by the user */ + sa6->sin6_addr.s6_addr16[1] = + htons(ifp->if_index); + } else + if (sa6->sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) + return(EINVAL); /* ifid is contradict */ + if (sa6->sin6_scope_id) { + if (sa6->sin6_scope_id != + (u_int32_t)ifp->if_index) + return(EINVAL); + sa6->sin6_scope_id = 0; /* XXX: good way? */ + } + } + } + ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); + + switch (cmd) { + + case SIOCDIFADDR_IN6: + if (ia == 0) + return(EADDRNOTAVAIL); + /* FALLTHROUGH */ + case SIOCAIFADDR_IN6: + case SIOCSIFADDR_IN6: + case SIOCSIFNETMASK_IN6: + case SIOCSIFDSTADDR_IN6: + if (!privileged) + return(EPERM); + if (ia == 0) { + ia = (struct in6_ifaddr *) + malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + if (ia == NULL) + return (ENOBUFS); + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + ia->ia_ifa.ifa_dstaddr + = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_ifa.ifa_netmask + = (struct sockaddr *)&ia->ia_prefixmask; + + ia->ia_ifp = ifp; + if ((oia = in6_ifaddr) != NULL) { + for ( ; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; + TAILQ_INSERT_TAIL(&ifp->if_addrlist, + (struct ifaddr *)ia, ifa_list); + if ((ifp->if_flags & IFF_LOOPBACK) == 0) + in6_interfaces++; /*XXX*/ + } + + if (cmd == SIOCAIFADDR_IN6) { + /* sanity for overflow - beware unsigned */ + struct in6_addrlifetime *lt; + lt = &ifra->ifra_lifetime; + if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME + && lt->ia6t_vltime + time_second < time_second) { + return EINVAL; + } + if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME + && lt->ia6t_pltime + time_second < time_second) { + return EINVAL; + } + } + break; + + case SIOCGIFADDR_IN6: + /* This interface is basically deprecated. use SIOCGIFCONF. */ + /* fall through */ + case SIOCGIFAFLAG_IN6: + case SIOCGIFNETMASK_IN6: + case SIOCGIFDSTADDR_IN6: + case SIOCGIFALIFETIME_IN6: + /* must think again about its semantics */ + if (ia == 0) + return(EADDRNOTAVAIL); + break; + case SIOCSIFALIFETIME_IN6: + { + struct in6_addrlifetime *lt; + + if (!privileged) + return(EPERM); + if (ia == 0) + return(EADDRNOTAVAIL); + /* sanity for overflow - beware unsigned */ + lt = &ifr->ifr_ifru.ifru_lifetime; + if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME + && lt->ia6t_vltime + time_second < time_second) { + return EINVAL; + } + if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME + && lt->ia6t_pltime + time_second < time_second) { + return EINVAL; + } + break; + } + } + + switch (cmd) { + + case SIOCGIFADDR_IN6: + ifr->ifr_addr = ia->ia_addr; + break; + + case SIOCGIFDSTADDR_IN6: + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) + return(EINVAL); + ifr->ifr_dstaddr = ia->ia_dstaddr; + break; + + case SIOCGIFNETMASK_IN6: + ifr->ifr_addr = ia->ia_prefixmask; + break; + + case SIOCGIFAFLAG_IN6: + ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags; + break; + + case SIOCGIFSTAT_IN6: + if (ifp == NULL) + return EINVAL; + if (in6_ifstat == NULL || ifp->if_index >= in6_ifstatmax + || in6_ifstat[ifp->if_index] == NULL) { + /* return EAFNOSUPPORT? */ + bzero(&ifr->ifr_ifru.ifru_stat, + sizeof(ifr->ifr_ifru.ifru_stat)); + } else + ifr->ifr_ifru.ifru_stat = *in6_ifstat[ifp->if_index]; + break; + + case SIOCGIFSTAT_ICMP6: + if (ifp == NULL) + return EINVAL; + if (icmp6_ifstat == NULL || ifp->if_index >= icmp6_ifstatmax || + icmp6_ifstat[ifp->if_index] == NULL) { + /* return EAFNOSUPPORT? */ + bzero(&ifr->ifr_ifru.ifru_stat, + sizeof(ifr->ifr_ifru.ifru_icmp6stat)); + } else + ifr->ifr_ifru.ifru_icmp6stat = + *icmp6_ifstat[ifp->if_index]; + break; + + case SIOCSIFDSTADDR_IN6: + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) + return(EINVAL); + oldaddr = ia->ia_dstaddr; + ia->ia_dstaddr = ifr->ifr_dstaddr; + + /* link-local index check */ + if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { + /* interface ID is not embedded by the user */ + ia->ia_dstaddr.sin6_addr.s6_addr16[1] + = htons(ifp->if_index); + } else + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } + } + + if (ifp->if_ioctl && (error = (ifp->if_ioctl) + (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { + ia->ia_dstaddr = oldaddr; + return(error); + } + if (ia->ia_flags & IFA_ROUTE) { + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; + rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); + ia->ia_ifa.ifa_dstaddr = + (struct sockaddr *)&ia->ia_dstaddr; + rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); + } + break; + + case SIOCGIFALIFETIME_IN6: + ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; + break; + + case SIOCSIFALIFETIME_IN6: + ia->ia6_lifetime = ifr->ifr_ifru.ifru_lifetime; + /* for sanity */ + if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_expire = + time_second + ia->ia6_lifetime.ia6t_vltime; + } else + ia->ia6_lifetime.ia6t_expire = 0; + if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_preferred = + time_second + ia->ia6_lifetime.ia6t_pltime; + } else + ia->ia6_lifetime.ia6t_preferred = 0; + break; + + case SIOCSIFADDR_IN6: + return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1)); + + case SIOCSIFNETMASK_IN6: + ia->ia_prefixmask = ifr->ifr_addr; + bzero(&net, sizeof(net)); + net.sin6_len = sizeof(struct sockaddr_in6); + net.sin6_family = AF_INET6; + net.sin6_port = htons(0); + net.sin6_flowinfo = htonl(0); + net.sin6_addr.s6_addr32[0] + = ia->ia_addr.sin6_addr.s6_addr32[0] & + ia->ia_prefixmask.sin6_addr.s6_addr32[0]; + net.sin6_addr.s6_addr32[1] + = ia->ia_addr.sin6_addr.s6_addr32[1] & + ia->ia_prefixmask.sin6_addr.s6_addr32[1]; + net.sin6_addr.s6_addr32[2] + = ia->ia_addr.sin6_addr.s6_addr32[2] & + ia->ia_prefixmask.sin6_addr.s6_addr32[2]; + net.sin6_addr.s6_addr32[3] + = ia->ia_addr.sin6_addr.s6_addr32[3] & + ia->ia_prefixmask.sin6_addr.s6_addr32[3]; + ia->ia_net = net; + break; + + case SIOCAIFADDR_IN6: + prefixIsNew = 0; + hostIsNew = 1; + + if (ifra->ifra_addr.sin6_len == 0) { + ifra->ifra_addr = ia->ia_addr; + hostIsNew = 0; + } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, + &ia->ia_addr.sin6_addr)) + hostIsNew = 0; + + if (ifra->ifra_prefixmask.sin6_len) { + in6_ifscrub(ifp, ia); + ia->ia_prefixmask = ifra->ifra_prefixmask; + prefixIsNew = 1; + } + if ((ifp->if_flags & IFF_POINTOPOINT) && + (ifra->ifra_dstaddr.sin6_family == AF_INET6)) { + in6_ifscrub(ifp, ia); + ia->ia_dstaddr = ifra->ifra_dstaddr; + /* link-local index check: should be a separate function? */ + if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { + /* + * interface ID is not embedded by + * the user + */ + ia->ia_dstaddr.sin6_addr.s6_addr16[1] + = htons(ifp->if_index); + } else + if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } + } + prefixIsNew = 1; /* We lie; but effect's the same */ + } + if (ifra->ifra_addr.sin6_family == AF_INET6 && + (hostIsNew || prefixIsNew)) + error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); + if (ifra->ifra_addr.sin6_family == AF_INET6 + && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { + int error_local = 0; + + /* + * join solicited multicast addr for new host id + */ + struct in6_addr llsol; + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = + ifra->ifra_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + (void)in6_addmulti(&llsol, ifp, &error_local); + if (error == 0) + error = error_local; + } + /* Join dstaddr's solicited multicast if necessary. */ + if (nd6_proxyall && hostIsNew) { + int error_local; + + error_local = in6_ifaddproxy(ia); + if (error == 0) + error = error_local; + } + + ia->ia6_flags = ifra->ifra_flags; + ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ + + ia->ia6_lifetime = ifra->ifra_lifetime; + /* for sanity */ + if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_expire = + time_second + ia->ia6_lifetime.ia6t_vltime; + } else + ia->ia6_lifetime.ia6t_expire = 0; + if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_preferred = + time_second + ia->ia6_lifetime.ia6t_pltime; + } else + ia->ia6_lifetime.ia6t_preferred = 0; + + /* + * Perform DAD, if needed. + * XXX It may be of use, if we can administratively + * disable DAD. + */ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + ia->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start((struct ifaddr *)ia, NULL); + break; +#ifdef IFT_DUMMY + case IFT_DUMMY: +#endif + case IFT_FAITH: + case IFT_GIF: + case IFT_LOOP: + default: + break; + } + + if (hostIsNew) { + int iilen; + int error_local = 0; + + iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - + in6_mask2len(&ia->ia_prefixmask.sin6_addr); + error_local = in6_prefix_add_ifid(iilen, ia); + if (error == 0) + error = error_local; + } + + return(error); + + case SIOCDIFADDR_IN6: + in6_ifscrub(ifp, ia); + + if (ifp->if_flags & IFF_MULTICAST) { + /* + * delete solicited multicast addr for deleting host id + */ + struct in6_multi *in6m; + struct in6_addr llsol; + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = + ia->ia_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } + /* Leave dstaddr's solicited multicast if necessary. */ + if (nd6_proxyall) + in6_ifremproxy(ia); + + TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + oia = ia; + if (oia == (ia = in6_ifaddr)) + in6_ifaddr = ia->ia_next; + else { + while (ia->ia_next && (ia->ia_next != oia)) + ia = ia->ia_next; + if (ia->ia_next) + ia->ia_next = oia->ia_next; + else + printf("Didn't unlink in6_ifaddr from list\n"); + } + { + int iilen; + + iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - + in6_mask2len(&oia->ia_prefixmask.sin6_addr); + in6_prefix_remove_ifid(iilen, oia); + } + IFAFREE((&oia->ia_ifa)); + break; + + default: + if (ifp == 0 || ifp->if_ioctl == 0) + return(EOPNOTSUPP); + return((*ifp->if_ioctl)(ifp, cmd, data)); + } + return(0); +} + +/* + * SIOC[GAD]LIFADDR. + * SIOCGLIFADDR: get first address. (???) + * SIOCGLIFADDR with IFLR_PREFIX: + * get first address that matches the specified prefix. + * SIOCALIFADDR: add the specified address. + * SIOCALIFADDR with IFLR_PREFIX: + * add the specified prefix, filling hostid part from + * the first link-local address. prefixlen must be <= 64. + * SIOCDLIFADDR: delete the specified address. + * SIOCDLIFADDR with IFLR_PREFIX: + * delete the first address that matches the specified prefix. + * return values: + * EINVAL on invalid parameters + * EADDRNOTAVAIL on prefix match failed/specified address not found + * other values may be returned from in6_ioctl() + * + * NOTE: SIOCALIFADDR(with IFLR_PREFIX set) allows prefixlen less than 64. + * this is to accomodate address naming scheme other than RFC2374, + * in the future. + * RFC2373 defines interface id to be 64bit, but it allows non-RFC2374 + * address encoding scheme. (see figure on page 8) + */ +static int +in6_lifaddr_ioctl(so, cmd, data, ifp, p) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; + struct proc *p; +{ + struct if_laddrreq *iflr = (struct if_laddrreq *)data; + struct ifaddr *ifa; + + /* sanity checks */ + if (!data || !ifp) { + panic("invalid argument to in6_lifaddr_ioctl"); + /*NOTRECHED*/ + } + + switch (cmd) { + case SIOCGLIFADDR: + /* address must be specified on GET with IFLR_PREFIX */ + if ((iflr->flags & IFLR_PREFIX) == 0) + break; + /*FALLTHROUGH*/ + case SIOCALIFADDR: + case SIOCDLIFADDR: + /* address must be specified on ADD and DELETE */ + if (iflr->addr.__ss_family != AF_INET6) + return EINVAL; + if (iflr->addr.__ss_len != sizeof(struct sockaddr_in6)) + return EINVAL; + /* XXX need improvement */ + if (iflr->dstaddr.__ss_family + && iflr->dstaddr.__ss_family != AF_INET6) + return EINVAL; + if (iflr->dstaddr.__ss_family + && iflr->dstaddr.__ss_len != sizeof(struct sockaddr_in6)) + return EINVAL; + break; + default: /*shouldn't happen*/ + return EOPNOTSUPP; + } + if (sizeof(struct in6_addr) * 8 < iflr->prefixlen) + return EINVAL; + + switch (cmd) { + case SIOCALIFADDR: + { + struct in6_aliasreq ifra; + struct in6_addr *hostid = NULL; + int prefixlen; + + if ((iflr->flags & IFLR_PREFIX) != 0) { + struct sockaddr_in6 *sin6; + + /* + * hostid is to fill in the hostid part of the + * address. hostid points to the first link-local + * address attached to the interface. + */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + if (!ifa) + return EADDRNOTAVAIL; + hostid = IFA_IN6(ifa); + + /* prefixlen must be <= 64. */ + if (64 < iflr->prefixlen) + return EINVAL; + prefixlen = iflr->prefixlen; + + /* hostid part must be zero. */ + sin6 = (struct sockaddr_in6 *)&iflr->addr; + if (sin6->sin6_addr.s6_addr32[2] != 0 + || sin6->sin6_addr.s6_addr32[3] != 0) { + return EINVAL; + } + } else + prefixlen = iflr->prefixlen; + + /* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ + bzero(&ifra, sizeof(ifra)); + bcopy(iflr->iflr_name, ifra.ifra_name, + sizeof(ifra.ifra_name)); + + bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.__ss_len); + if (hostid) { + /* fill in hostid part */ + ifra.ifra_addr.sin6_addr.s6_addr32[2] = + hostid->s6_addr32[2]; + ifra.ifra_addr.sin6_addr.s6_addr32[3] = + hostid->s6_addr32[3]; + } + + if (iflr->dstaddr.__ss_family) { /*XXX*/ + bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, + iflr->dstaddr.__ss_len); + if (hostid) { + ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] = + hostid->s6_addr32[2]; + ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] = + hostid->s6_addr32[3]; + } + } + + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); + + ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX; + return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p); + } + case SIOCGLIFADDR: + case SIOCDLIFADDR: + { + struct in6_ifaddr *ia; + struct in6_addr mask, candidate, match; + struct sockaddr_in6 *sin6; + int cmp; + + bzero(&mask, sizeof(mask)); + if (iflr->flags & IFLR_PREFIX) { + /* lookup a prefix rather than address. */ + in6_len2mask(&mask, iflr->prefixlen); + + sin6 = (struct sockaddr_in6 *)&iflr->addr; + bcopy(&sin6->sin6_addr, &match, sizeof(match)); + match.s6_addr32[0] &= mask.s6_addr32[0]; + match.s6_addr32[1] &= mask.s6_addr32[1]; + match.s6_addr32[2] &= mask.s6_addr32[2]; + match.s6_addr32[3] &= mask.s6_addr32[3]; + + /* if you set extra bits, that's wrong */ + if (bcmp(&match, &sin6->sin6_addr, sizeof(match))) + return EINVAL; + + cmp = 1; + } else { + if (cmd == SIOCGLIFADDR) { + /* on getting an address, take the 1st match */ + cmp = 0; /*XXX*/ + } else { + /* on deleting an address, do exact match */ + in6_len2mask(&mask, 128); + sin6 = (struct sockaddr_in6 *)&iflr->addr; + bcopy(&sin6->sin6_addr, &match, sizeof(match)); + + cmp = 1; + } + } + + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (!cmp) + break; + bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); + candidate.s6_addr32[0] &= mask.s6_addr32[0]; + candidate.s6_addr32[1] &= mask.s6_addr32[1]; + candidate.s6_addr32[2] &= mask.s6_addr32[2]; + candidate.s6_addr32[3] &= mask.s6_addr32[3]; + if (IN6_ARE_ADDR_EQUAL(&candidate, &match)) + break; + } + if (!ifa) + return EADDRNOTAVAIL; + ia = ifa2ia6(ifa); + + if (cmd == SIOCGLIFADDR) { + /* fill in the if_laddrreq structure */ + bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); + + if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { + bcopy(&ia->ia_dstaddr, &iflr->dstaddr, + ia->ia_dstaddr.sin6_len); + } else + bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); + + iflr->prefixlen = + in6_mask2len(&ia->ia_prefixmask.sin6_addr); + + iflr->flags = ia->ia6_flags; /*XXX*/ + + return 0; + } else { + struct in6_aliasreq ifra; + + /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ + bzero(&ifra, sizeof(ifra)); + bcopy(iflr->iflr_name, ifra.ifra_name, + sizeof(ifra.ifra_name)); + + bcopy(&ia->ia_addr, &ifra.ifra_addr, + ia->ia_addr.sin6_len); + if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { + bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, + ia->ia_dstaddr.sin6_len); + } + bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr, + ia->ia_prefixmask.sin6_len); + + ifra.ifra_flags = ia->ia6_flags; + return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, + ifp, p); + } + } + } + + return EOPNOTSUPP; /*just for safety*/ +} + +/* + * Delete any existing route for an interface. + */ +void +in6_ifscrub(ifp, ia) + register struct ifnet *ifp; + register struct in6_ifaddr *ia; +{ + if ((ia->ia_flags & IFA_ROUTE) == 0) + return; + if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); + else + rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); + ia->ia_flags &= ~IFA_ROUTE; + + /* Remove ownaddr's loopback rtentry, if it exists. */ + in6_ifremloop(&(ia->ia_ifa)); +} + +/* + * Initialize an interface's intetnet6 address + * and routing table entry. + */ +int +in6_ifinit(ifp, ia, sin6, scrub) + struct ifnet *ifp; + struct in6_ifaddr *ia; + struct sockaddr_in6 *sin6; + int scrub; +{ + struct sockaddr_in6 oldaddr; + int error, flags = RTF_UP; + int s = splimp(); + + oldaddr = ia->ia_addr; + ia->ia_addr = *sin6; + /* + * Give the interface a chance to initialize + * if this is its first address, + * and to validate the address if necessary. + */ + if (ifp->if_ioctl && + (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { + splx(s); + ia->ia_addr = oldaddr; + return(error); + } + + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + break; + case IFT_PPP: + ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + break; + } + + splx(s); + if (scrub) { + ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; + in6_ifscrub(ifp, ia); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + } + /* xxx + * in_socktrim + */ + /* + * Add route for the network. + */ + ia->ia_ifa.ifa_metric = ifp->if_metric; + if (ifp->if_flags & IFF_LOOPBACK) { + ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; + flags |= RTF_HOST; + } else if (ifp->if_flags & IFF_POINTOPOINT) { + if (ia->ia_dstaddr.sin6_family != AF_INET6) + return(0); + flags |= RTF_HOST; + } + if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) + ia->ia_flags |= IFA_ROUTE; + + /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ + in6_ifaddloop(&(ia->ia_ifa)); + + return(error); +} + +/* + * Add an address to the list of IP6 multicast addresses for a + * given interface. + */ +struct in6_multi * +in6_addmulti(maddr6, ifp, errorp) + register struct in6_addr *maddr6; + register struct ifnet *ifp; + int *errorp; +{ + struct in6_multi *in6m; + struct sockaddr_in6 sin6; + struct ifmultiaddr *ifma; + int s = splnet(); + + *errorp = 0; + + /* + * Call generic routine to add membership or increment + * refcount. It wants addresses in the form of a sockaddr, + * so we build one here (being careful to zero the unused bytes). + */ + bzero(&sin6, sizeof sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof sin6; + sin6.sin6_addr = *maddr6; + *errorp = if_addmulti(ifp, (struct sockaddr *)&sin6, &ifma); + if (*errorp) { + splx(s); + return 0; + } + + /* + * If ifma->ifma_protospec is null, then if_addmulti() created + * a new record. Otherwise, we are done. + */ + if (ifma->ifma_protospec != 0) + return ifma->ifma_protospec; + + /* XXX - if_addmulti uses M_WAITOK. Can this really be called + at interrupt time? If so, need to fix if_addmulti. XXX */ + in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); + if (in6m == NULL) { + splx(s); + return (NULL); + } + + bzero(in6m, sizeof *in6m); + in6m->in6m_addr = *maddr6; + in6m->in6m_ifp = ifp; + in6m->in6m_ifma = ifma; + ifma->ifma_protospec = in6m; + LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry); + + /* + * Let MLD6 know that we have joined a new IP6 multicast + * group. + */ + mld6_start_listening(in6m); + splx(s); + return(in6m); +} + +/* + * Delete a multicast address record. + */ +void +in6_delmulti(in6m) + struct in6_multi *in6m; +{ + struct ifmultiaddr *ifma = in6m->in6m_ifma; + int s = splnet(); + + if (ifma->ifma_refcount == 1) { + /* + * No remaining claims to this record; let MLD6 know + * that we are leaving the multicast group. + */ + mld6_stop_listening(in6m); + ifma->ifma_protospec = 0; + LIST_REMOVE(in6m, in6m_entry); + free(in6m, M_IPMADDR); + } + /* XXX - should be separate API for when we have an ifma? */ + if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); + splx(s); +} + +/* + * Find an IPv6 interface link-local address specific to an interface. + */ +struct in6_ifaddr * +in6ifa_ifpforlinklocal(ifp) + struct ifnet *ifp; +{ + register struct ifaddr *ifa; + + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr == NULL) + continue; /* just for safety */ + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) + break; + } + + return((struct in6_ifaddr *)ifa); +} + + +/* + * find the internet address corresponding to a given interface and address. + */ +struct in6_ifaddr * +in6ifa_ifpwithaddr(ifp, addr) + struct ifnet *ifp; + struct in6_addr *addr; +{ + register struct ifaddr *ifa; + + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr == NULL) + continue; /* just for safety */ + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) + break; + } + + return((struct in6_ifaddr *)ifa); +} + +/* + * Convert IP6 address to printable (loggable) representation. + */ +static char digits[] = "0123456789abcdef"; +static int ip6round = 0; +char * +ip6_sprintf(addr) +register struct in6_addr *addr; +{ + static char ip6buf[8][48]; + register int i; + register char *cp; + register u_short *a = (u_short *)addr; + register u_char *d; + int dcolon = 0; + + ip6round = (ip6round + 1) & 7; + cp = ip6buf[ip6round]; + + for (i = 0; i < 8; i++) { + if (dcolon == 1) { + if (*a == 0) { + if (i == 7) + *cp++ = ':'; + a++; + continue; + } else + dcolon = 2; + } + if (*a == 0) { + if (dcolon == 0 && *(a + 1) == 0) { + if (i == 0) + *cp++ = ':'; + *cp++ = ':'; + dcolon = 1; + } else { + *cp++ = '0'; + *cp++ = ':'; + } + a++; + continue; + } + d = (u_char *)a; + *cp++ = digits[*d >> 4]; + *cp++ = digits[*d++ & 0xf]; + *cp++ = digits[*d >> 4]; + *cp++ = digits[*d & 0xf]; + *cp++ = ':'; + a++; + } + *--cp = 0; + return(ip6buf[ip6round]); +} + +int +in6_localaddr(in6) + struct in6_addr *in6; +{ + struct in6_ifaddr *ia; + + if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) + return 1; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) + if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, + &ia->ia_prefixmask.sin6_addr)) + return 1; + + return (0); +} + +/* + * Get a scope of the address. Node-local, link-local, site-local or global. + */ +int +in6_addrscope (addr) +struct in6_addr *addr; +{ + int scope; + + if (addr->s6_addr8[0] == 0xfe) { + scope = addr->s6_addr8[1] & 0xc0; + + switch (scope) { + case 0x80: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case 0xc0: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ + break; + } + } + + + if (addr->s6_addr8[0] == 0xff) { + scope = addr->s6_addr8[1] & 0x0f; + + /* + * due to other scope such as reserved, + * return scope doesn't work. + */ + switch (scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return IPV6_ADDR_SCOPE_NODELOCAL; + break; + case IPV6_ADDR_SCOPE_LINKLOCAL: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; + break; + } + } + + if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { + if (addr->s6_addr8[15] == 1) /* loopback */ + return IPV6_ADDR_SCOPE_NODELOCAL; + if (addr->s6_addr8[15] == 0) /* unspecified */ + return IPV6_ADDR_SCOPE_LINKLOCAL; + } + + return IPV6_ADDR_SCOPE_GLOBAL; +} + +/* + * return length of part which dst and src are equal + * hard coding... + */ + +int +in6_matchlen(src, dst) +struct in6_addr *src, *dst; +{ + int match = 0; + u_char *s = (u_char *)src, *d = (u_char *)dst; + u_char *lim = s + 16, r; + + while (s < lim) + if ((r = (*d++ ^ *s++)) != 0) { + while (r < 128) { + match++; + r <<= 1; + } + break; + } else + match += 8; + return match; +} + +int +in6_are_prefix_equal(p1, p2, len) + struct in6_addr *p1, *p2; + int len; +{ + int bytelen, bitlen; + + /* sanity check */ + if (0 > len || len > 128) { + log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n", + len); + return(0); + } + + bytelen = len / 8; + bitlen = len % 8; + + if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) + return(0); + if (p1->s6_addr[bytelen] >> (8 - bitlen) != + p2->s6_addr[bytelen] >> (8 - bitlen)) + return(0); + + return(1); +} + +void +in6_prefixlen2mask(maskp, len) + struct in6_addr *maskp; + int len; +{ + u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; + int bytelen, bitlen, i; + + /* sanity check */ + if (0 > len || len > 128) { + log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n", + len); + return; + } + + bzero(maskp, sizeof(*maskp)); + bytelen = len / 8; + bitlen = len % 8; + for (i = 0; i < bytelen; i++) + maskp->s6_addr[i] = 0xff; + if (bitlen) + maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; +} + +/* + * return the best address out of the same scope + */ + +struct in6_ifaddr * +in6_ifawithscope(ifp, dst) + register struct ifnet *ifp; + register struct in6_addr *dst; +{ + int dst_scope = in6_addrscope(dst), blen = -1, tlen; + struct ifaddr *ifa; + struct in6_ifaddr *besta = NULL, *ia; + struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/ + + dep[0] = dep[1] = NULL; + + /* + * We first look for addresses in the same scope. + * If there is one, return it. + * If two or more, return one which matches the dst longest. + * If none, return one of global addresses assigned other ifs. + */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) + continue; /* XXX: is there any case to allow anycast? */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) + continue; /* don't use this interface */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) + continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { + if (ip6_use_deprecated) + dep[0] = (struct in6_ifaddr *)ifa; + continue; + } + + if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { + /* + * call in6_matchlen() as few as possible + */ + if (besta) { + if (blen == -1) + blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); + tlen = in6_matchlen(IFA_IN6(ifa), dst); + if (tlen > blen) { + blen = tlen; + besta = (struct in6_ifaddr *)ifa; + } + } else + besta = (struct in6_ifaddr *)ifa; + } + } + if (besta) + return besta; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IPV6_ADDR_SCOPE_GLOBAL != + in6_addrscope(&(ia->ia_addr.sin6_addr))) + continue; + /* XXX: is there any case to allow anycast? */ + if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) + continue; + if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) + continue; + if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { + if (ip6_use_deprecated) + dep[1] = (struct in6_ifaddr *)ifa; + continue; + } + return ia; + } + + /* use the last-resort values, that are, deprecated addresses */ + if (dep[0]) + return dep[0]; + if (dep[1]) + return dep[1]; + + return NULL; +} + +/* + * return the best address out of the same scope. if no address was + * found, return the first valid address from designated IF. + */ + +struct in6_ifaddr * +in6_ifawithifp(ifp, dst) + register struct ifnet *ifp; + register struct in6_addr *dst; +{ + int dst_scope = in6_addrscope(dst), blen = -1, tlen; + struct ifaddr *ifa; + struct in6_ifaddr *besta = 0; + struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/ + + dep[0] = dep[1] = NULL; + + /* + * We first look for addresses in the same scope. + * If there is one, return it. + * If two or more, return one which matches the dst longest. + * If none, return one of global addresses assigned other ifs. + */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) + continue; /* XXX: is there any case to allow anycast? */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) + continue; /* don't use this interface */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) + continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { + if (ip6_use_deprecated) + dep[0] = (struct in6_ifaddr *)ifa; + continue; + } + + if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { + /* + * call in6_matchlen() as few as possible + */ + if (besta) { + if (blen == -1) + blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); + tlen = in6_matchlen(IFA_IN6(ifa), dst); + if (tlen > blen) { + blen = tlen; + besta = (struct in6_ifaddr *)ifa; + } + } else + besta = (struct in6_ifaddr *)ifa; + } + } + if (besta) + return(besta); + + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) + continue; /* XXX: is there any case to allow anycast? */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) + continue; /* don't use this interface */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) + continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { + if (ip6_use_deprecated) + dep[1] = (struct in6_ifaddr *)ifa; + continue; + } + + return (struct in6_ifaddr *)ifa; + } + + /* use the last-resort values, that are, deprecated addresses */ + if (dep[0]) + return dep[0]; + if (dep[1]) + return dep[1]; + + return NULL; +} + +/* + * perform DAD when interface becomes IFF_UP. + */ +void +in6_if_up(ifp) + struct ifnet *ifp; +{ + struct ifaddr *ifa; + struct in6_ifaddr *ia; + struct sockaddr_dl *sdl; + int type; + struct ether_addr ea; + int off; + int dad_delay; /* delay ticks before DAD output */ + + bzero(&ea, sizeof(ea)); + sdl = NULL; + + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { + goto dad; + } + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + break; + } + + switch (ifp->if_type) { + case IFT_SLIP: + case IFT_PPP: +#ifdef IFT_DUMMY + case IFT_DUMMY: +#endif + case IFT_GIF: + case IFT_FAITH: + type = IN6_IFT_P2P; + in6_ifattach(ifp, type, 0, 1); + break; + case IFT_ETHER: + case IFT_FDDI: + case IFT_ATM: + type = IN6_IFT_802; + if (sdl == NULL) + break; + off = sdl->sdl_nlen; + if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0) + in6_ifattach(ifp, type, LLADDR(sdl), 0); + break; + case IFT_ARCNET: + type = IN6_IFT_ARCNET; + if (sdl == NULL) + break; + off = sdl->sdl_nlen; + if (sdl->sdl_data[off] != 0) /* XXX ?: */ + in6_ifattach(ifp, type, LLADDR(sdl), 0); + break; + default: + break; + } + +dad: + dad_delay = 0; + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ia = (struct in6_ifaddr *)ifa; + if (ia->ia6_flags & IN6_IFF_TENTATIVE) + nd6_dad_start(ifa, &dad_delay); + } +} + +/* + * Calculate max IPv6 MTU through all the interfaces and store it + * to in6_maxmtu. + */ +void +in6_setmaxmtu() +{ + unsigned long maxmtu = 0; + struct ifnet *ifp; + + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) + { + if ((ifp->if_flags & IFF_LOOPBACK) == 0 && + nd_ifinfo[ifp->if_index].linkmtu > maxmtu) + maxmtu = nd_ifinfo[ifp->if_index].linkmtu; + } + if (maxmtu) /* update only when maxmtu is positive */ + in6_maxmtu = maxmtu; +} + +/* + * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be + * v4 mapped addr or v4 compat addr + */ +void +in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) +{ + bzero(sin, sizeof(*sin)); + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_family = AF_INET; + sin->sin_port = sin6->sin6_port; + sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; +} + +/* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */ +void +in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) +{ + bzero(sin6, sizeof(*sin6)); + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = sin->sin_port; + sin6->sin6_addr.s6_addr32[0] = 0; + sin6->sin6_addr.s6_addr32[1] = 0; + sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP; + sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; +} + +/* Convert sockaddr_in6 into sockaddr_in. */ +void +in6_sin6_2_sin_in_sock(struct sockaddr *nam) +{ + struct sockaddr_in *sin_p; + struct sockaddr_in6 sin6; + + /* + * Save original sockaddr_in6 addr and convert it + * to sockaddr_in. + */ + sin6 = *(struct sockaddr_in6 *)nam; + sin_p = (struct sockaddr_in *)nam; + in6_sin6_2_sin(sin_p, &sin6); +} + +/* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */ +void +in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam) +{ + struct sockaddr_in *sin_p; + struct sockaddr_in6 *sin6_p; + + MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME, + M_WAITOK); + sin_p = (struct sockaddr_in *)*nam; + in6_sin_2_v4mapsin6(sin_p, sin6_p); + FREE(*nam, M_SONAME); + *nam = (struct sockaddr *)sin6_p; +} diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 97b0c7e..a934e9b 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -13,7 +13,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -516,22 +516,10 @@ struct in6_pktinfo { #define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */ #define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */ #define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */ -#ifdef MAPPED_ADDR_ENABLED #define IPV6CTL_MAPPED_ADDR 23 -#endif /* MAPPED_ADDR_ENABLED */ /* New entries should be added here from current IPV6CTL_MAXID value. */ #define IPV6CTL_MAXID 24 -#ifdef MAPPED_ADDR_ENABLED -#define IPV6CTL_NAMES_MAPPED_ADDR "mapped_addr" -#define IPV6CTL_TYPE_MAPPED_ADDR CTLTYPE_INT -#define IPV6CTL_VARS_MAPPED_ADDR &ip6_mapped_addr_on -#else /* MAPPED_ADDR_ENABLED */ -#define IPV6CTL_NAMES_MAPPED_ADDR 0 -#define IPV6CTL_TYPE_MAPPED_ADDR 0 -#define IPV6CTL_VARS_MAPPED_ADDR 0 -#endif /* MAPPED_ADDR_ENABLED */ - #define IPV6CTL_NAMES { \ { 0, 0 }, \ { "forwarding", CTLTYPE_INT }, \ @@ -556,7 +544,7 @@ struct in6_pktinfo { { 0, 0 }, \ { "use_deprecated", CTLTYPE_INT }, \ { "rr_prune", CTLTYPE_INT }, \ - { IPV6CTL_NAMES_MAPPED_ADDR, IPV6CTL_TYPE_MAPPED_ADDR }, \ + { "mapped_addr", CTLTYPE_INT }, \ } #define IPV6CTL_VARS { \ @@ -583,12 +571,23 @@ struct in6_pktinfo { 0, \ &ip6_use_deprecated, \ &ip6_rr_prune, \ - IPV6CTL_VARS_MAPPED_ADDR, \ + &ip6_mapped_addr_on, \ } #endif /* !_XOPEN_SOURCE */ +/* + * Redefinition of mbuf flags + */ +#define M_ANYCAST6 M_PROTO1 +#define M_AUTHIPHDR M_PROTO2 +#define M_DECRYPTED M_PROTO3 +#define M_LOOP M_PROTO4 +#define M_AUTHIPDGM M_PROTO5 + #ifdef _KERNEL -struct cmsghdr; +struct cmsghdr; +struct mbuf; +struct ifnet; int in6_canforward __P((struct in6_addr *, struct in6_addr *)); int in6_cksum __P((struct mbuf *, u_int8_t, int, int)); @@ -597,7 +596,6 @@ int in6_addrscope __P((struct in6_addr *)); struct in6_ifaddr *in6_ifawithscope __P((struct ifnet *, struct in6_addr *)); struct in6_ifaddr *in6_ifawithifp __P((struct ifnet *, struct in6_addr *)); extern void in6_if_up __P((struct ifnet *)); -#ifdef MAPPED_ADDR_ENABLED struct sockaddr; void in6_sin6_2_sin __P((struct sockaddr_in *sin, @@ -606,7 +604,6 @@ void in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin, struct sockaddr_in6 *sin6)); void in6_sin6_2_sin_in_sock __P((struct sockaddr *nam)); void in6_sin_2_v4mapsin6_in_sock __P((struct sockaddr **nam)); -#endif /* MAPPED_ADDR_ENABLED */ #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) #define sin6tosa(sin6) ((struct sockaddr *)(sin6)) diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c new file mode 100644 index 0000000..28af01f --- /dev/null +++ b/sys/netinet6/in6_cksum.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1988, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 + */ + +#include +#include +#include +#include +#include + +#include + +/* + * Checksum routine for Internet Protocol family headers (Portable Version). + * + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + */ + +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} + +static union { + u_int16_t phs[4]; + struct { + u_int32_t ph_len; + u_int8_t ph_zero[3]; + u_int8_t ph_nxt; + } ph; +} uph; + +/* + * m MUST contain a continuous IP6 header. + * off is a offset where TCP/UDP/ICMP6 header starts. + * len is a total length of a transport segment. + * (e.g. TCP header + TCP payload) + */ + +int +in6_cksum(m, nxt, off, len) + register struct mbuf *m; + u_int8_t nxt; + register int off, len; +{ + register u_int16_t *w; + register int sum = 0; + register int mlen = 0; + int byte_swapped = 0; + struct ip6_hdr *ip6; + + union { + u_int8_t c[2]; + u_int16_t s; + } s_util; + union { + u_int16_t s[2]; + u_int32_t l; + } l_util; + + /* sanity check */ + if (m->m_pkthdr.len < off + len) { + panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)\n", + m->m_pkthdr.len, off, len); + } + + /* + * First create IP6 pseudo header and calculate a summary. + */ + ip6 = mtod(m, struct ip6_hdr *); + w = (u_int16_t *)&ip6->ip6_src; + uph.ph.ph_len = htonl(len); + uph.ph.ph_nxt = nxt; + + /* IPv6 source address */ + sum += w[0]; + if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + sum += w[1]; + sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; + sum += w[6]; sum += w[7]; + /* IPv6 destination address */ + sum += w[8]; + if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + sum += w[9]; + sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13]; + sum += w[14]; sum += w[15]; + /* Payload length and upper layer identifier */ + sum += uph.phs[0]; sum += uph.phs[1]; + sum += uph.phs[2]; sum += uph.phs[3]; + + /* + * Secondly calculate a summary of the first mbuf excluding offset. + */ + while (m != NULL && off > 0) { + if (m->m_len <= off) + off -= m->m_len; + else + break; + m = m->m_next; + } + w = (u_int16_t *)(mtod(m, u_char *) + off); + mlen = m->m_len - off; + if (len < mlen) + mlen = len; + len -= mlen; + /* + * Force to even boundary. + */ + if ((1 & (long) w) && (mlen > 0)) { + REDUCE; + sum <<= 8; + s_util.c[0] = *(u_char *)w; + w = (u_int16_t *)((char *)w + 1); + mlen--; + byte_swapped = 1; + } + /* + * Unroll the loop to make overhead from + * branches &c small. + */ + while ((mlen -= 32) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; + sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; + sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; + w += 16; + } + mlen += 32; + while ((mlen -= 8) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + w += 4; + } + mlen += 8; + if (mlen == 0 && byte_swapped == 0) + goto next; + REDUCE; + while ((mlen -= 2) >= 0) { + sum += *w++; + } + if (byte_swapped) { + REDUCE; + sum <<= 8; + byte_swapped = 0; + if (mlen == -1) { + s_util.c[1] = *(char *)w; + sum += s_util.s; + mlen = 0; + } else + mlen = -1; + } else if (mlen == -1) + s_util.c[0] = *(char *)w; + next: + m = m->m_next; + + /* + * Lastly calculate a summary of the rest of mbufs. + */ + + for (;m && len; m = m->m_next) { + if (m->m_len == 0) + continue; + w = mtod(m, u_int16_t *); + if (mlen == -1) { + /* + * The first byte of this mbuf is the continuation + * of a word spanning between this mbuf and the + * last mbuf. + * + * s_util.c[0] is already saved when scanning previous + * mbuf. + */ + s_util.c[1] = *(char *)w; + sum += s_util.s; + w = (u_int16_t *)((char *)w + 1); + mlen = m->m_len - 1; + len--; + } else + mlen = m->m_len; + if (len < mlen) + mlen = len; + len -= mlen; + /* + * Force to even boundary. + */ + if ((1 & (long) w) && (mlen > 0)) { + REDUCE; + sum <<= 8; + s_util.c[0] = *(u_char *)w; + w = (u_int16_t *)((char *)w + 1); + mlen--; + byte_swapped = 1; + } + /* + * Unroll the loop to make overhead from + * branches &c small. + */ + while ((mlen -= 32) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; + sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; + sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; + w += 16; + } + mlen += 32; + while ((mlen -= 8) >= 0) { + sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; + w += 4; + } + mlen += 8; + if (mlen == 0 && byte_swapped == 0) + continue; + REDUCE; + while ((mlen -= 2) >= 0) { + sum += *w++; + } + if (byte_swapped) { + REDUCE; + sum <<= 8; + byte_swapped = 0; + if (mlen == -1) { + s_util.c[1] = *(char *)w; + sum += s_util.s; + mlen = 0; + } else + mlen = -1; + } else if (mlen == -1) + s_util.c[0] = *(char *)w; + } + if (len) + panic("in6_cksum: out of data\n"); + if (mlen == -1) { + /* The last mbuf has odd # of bytes. Follow the + standard (the odd byte may be shifted left by 8 bits + or not as determined by endian-ness of the machine) */ + s_util.c[1] = 0; + sum += s_util.s; + } + REDUCE; + return (~sum & 0xffff); +} diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c new file mode 100644 index 0000000..85ca75f --- /dev/null +++ b/sys/netinet6/in6_ifattach.c @@ -0,0 +1,690 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct in6_addr llsol; + +struct in6_ifstat **in6_ifstat = NULL; +struct icmp6_ifstat **icmp6_ifstat = NULL; +size_t in6_ifstatmax = 0; +size_t icmp6_ifstatmax = 0; +unsigned long in6_maxmtu = 0; + +int found_first_ifid = 0; +#define IFID_LEN 8 +static char first_ifid[IFID_LEN]; + +static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t)); +static int gen_rand_eui64 __P((u_int8_t *)); + +static int +laddr_to_eui64(dst, src, len) + u_int8_t *dst; + u_int8_t *src; + size_t len; +{ + static u_int8_t zero[8]; + + bzero(zero, sizeof(zero)); + + switch (len) { + case 6: + if (bcmp(zero, src, 6) == 0) + return EINVAL; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = 0xff; + dst[4] = 0xfe; + dst[5] = src[3]; + dst[6] = src[4]; + dst[7] = src[5]; + break; + case 8: + if (bcmp(zero, src, 8) == 0) + return EINVAL; + bcopy(src, dst, len); + break; + default: + return EINVAL; + } + + return 0; +} + +/* + * Generate a last-resort interface identifier, when the machine has no + * IEEE802/EUI64 address sources. + * The address should be random, and should not change across reboot. + */ +static int +gen_rand_eui64(dst) + u_int8_t *dst; +{ + MD5_CTX ctxt; + u_int8_t digest[16]; + int hostnamelen = strlen(hostname); + + /* generate 8bytes of pseudo-random value. */ + bzero(&ctxt, sizeof(ctxt)); + MD5Init(&ctxt); + MD5Update(&ctxt, hostname, hostnamelen); + MD5Final(digest, &ctxt); + + /* assumes sizeof(digest) > sizeof(first_ifid) */ + bcopy(digest, dst, 8); + + /* make sure to set "u" bit to local, and "g" bit to individual. */ + dst[0] &= 0xfe; + dst[0] |= 0x02; /* EUI64 "local" */ + + return 0; +} + +/* + * Find first ifid on list of interfaces. + * This is assumed that ifp0's interface token (for example, IEEE802 MAC) + * is globally unique. We may need to have a flag parameter in the future. + */ +int +in6_ifattach_getifid(ifp0) + struct ifnet *ifp0; +{ + struct ifnet *ifp; + struct ifaddr *ifa; + u_int8_t *addr = NULL; + int addrlen = 0; + struct sockaddr_dl *sdl; + + if (found_first_ifid) + return 0; + + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) + { + if (ifp0 != NULL && ifp0 != ifp) + continue; + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl == NULL) + continue; + if (sdl->sdl_alen == 0) + continue; + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: + case IFT_ATM: + /* IEEE802/EUI64 cases - what others? */ + addr = LLADDR(sdl); + addrlen = sdl->sdl_alen; + /* + * to copy ifid from IEEE802/EUI64 interface, + * u bit of the source needs to be 0. + */ + if ((addr[0] & 0x02) != 0) + break; + goto found; + case IFT_ARCNET: + /* + * ARCnet interface token cannot be used as + * globally unique identifier due to its + * small bitwidth. + */ + break; + default: + break; + } + } + } +#ifdef DEBUG + printf("in6_ifattach_getifid: failed to get EUI64"); +#endif + return EADDRNOTAVAIL; + +found: + if (laddr_to_eui64(first_ifid, addr, addrlen) == 0) + found_first_ifid = 1; + + if (found_first_ifid) { + printf("%s: supplying EUI64: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + if_name(ifp), + first_ifid[0] & 0xff, first_ifid[1] & 0xff, + first_ifid[2] & 0xff, first_ifid[3] & 0xff, + first_ifid[4] & 0xff, first_ifid[5] & 0xff, + first_ifid[6] & 0xff, first_ifid[7] & 0xff); + + /* invert u bit to convert EUI64 to RFC2373 interface ID. */ + first_ifid[0] ^= 0x02; + + return 0; + } else { +#ifdef DEBUG + printf("in6_ifattach_getifid: failed to get EUI64"); +#endif + return EADDRNOTAVAIL; + } +} + +/* + * add link-local address to *pseudo* p2p interfaces. + * get called when the first MAC address is made available in in6_ifattach(). + * + * XXX I start considering this loop as a bad idea. (itojun) + */ +void +in6_ifattach_p2p() +{ + struct ifnet *ifp; + + /* prevent infinite loop. just in case. */ + if (found_first_ifid == 0) + return; + + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) + { + switch (ifp->if_type) { + case IFT_GIF: + /* pseudo interfaces - safe to initialize here */ + in6_ifattach(ifp, IN6_IFT_P2P, 0, 0); + break; +#ifdef IFT_DUMMY + case IFT_DUMMY: +#endif + case IFT_FAITH: + /* this mistakingly becomes IFF_UP */ + break; + case IFT_SLIP: + /* IPv6 is not supported */ + break; + case IFT_PPP: + /* this is not a pseudo interface, skip it */ + break; + default: + break; + } + } +} + +void +in6_ifattach(ifp, type, laddr, noloop) + struct ifnet *ifp; + u_int type; + caddr_t laddr; + /* size_t laddrlen; */ + int noloop; +{ + static size_t if_indexlim = 8; + struct sockaddr_in6 mltaddr; + struct sockaddr_in6 mltmask; + struct sockaddr_in6 gate; + struct sockaddr_in6 mask; + + struct in6_ifaddr *ia, *ib, *oia; + struct ifaddr *ifa; + int rtflag = 0; + + if (type == IN6_IFT_P2P && found_first_ifid == 0) { + printf("%s: no ifid available for IPv6 link-local address\n", + if_name(ifp)); + /* last resort */ + if (gen_rand_eui64(first_ifid) == 0) { + printf("%s: using random value as EUI64: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + if_name(ifp), + first_ifid[0] & 0xff, first_ifid[1] & 0xff, + first_ifid[2] & 0xff, first_ifid[3] & 0xff, + first_ifid[4] & 0xff, first_ifid[5] & 0xff, + first_ifid[6] & 0xff, first_ifid[7] & 0xff); + /* + * invert u bit to convert EUI64 to RFC2373 interface + * ID. + */ + first_ifid[0] ^= 0x02; + + found_first_ifid = 1; + } + } + + if ((ifp->if_flags & IFF_MULTICAST) == 0) { + printf("%s: not multicast capable, IPv6 not enabled\n", + if_name(ifp)); + return; + } + + /* + * We have some arrays that should be indexed by if_index. + * since if_index will grow dynamically, they should grow too. + * struct in6_ifstat **in6_ifstat + * struct icmp6_ifstat **icmp6_ifstat + */ + if (in6_ifstat == NULL || icmp6_ifstat == NULL + || if_index >= if_indexlim) { + size_t n; + caddr_t q; + size_t olim; + + olim = if_indexlim; + while (if_index >= if_indexlim) + if_indexlim <<= 1; + + /* grow in6_ifstat */ + n = if_indexlim * sizeof(struct in6_ifstat *); + q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); + bzero(q, n); + if (in6_ifstat) { + bcopy((caddr_t)in6_ifstat, q, + olim * sizeof(struct in6_ifstat *)); + free((caddr_t)in6_ifstat, M_IFADDR); + } + in6_ifstat = (struct in6_ifstat **)q; + in6_ifstatmax = if_indexlim; + + /* grow icmp6_ifstat */ + n = if_indexlim * sizeof(struct icmp6_ifstat *); + q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); + bzero(q, n); + if (icmp6_ifstat) { + bcopy((caddr_t)icmp6_ifstat, q, + olim * sizeof(struct icmp6_ifstat *)); + free((caddr_t)icmp6_ifstat, M_IFADDR); + } + icmp6_ifstat = (struct icmp6_ifstat **)q; + icmp6_ifstatmax = if_indexlim; + } + + /* + * To prevent to assign link-local address to PnP network + * cards multiple times. + * This is lengthy for P2P and LOOP but works. + */ + ifa = TAILQ_FIRST(&ifp->if_addrlist); + if (ifa != NULL) { + for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr)) + return; + } + } else { + TAILQ_INIT(&ifp->if_addrlist); + } + + /* + * link-local address + */ + ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; + ia->ia_ifp = ifp; + TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + /* + * Also link into the IPv6 address chain beginning with in6_ifaddr. + * kazu opposed it, but itojun & jinmei wanted. + */ + if ((oia = in6_ifaddr) != NULL) { + for (; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; + + ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_prefixmask.sin6_family = AF_INET6; + ia->ia_prefixmask.sin6_addr = in6mask64; + + bzero(&ia->ia_addr, sizeof(struct sockaddr_in6)); + ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); + ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + ia->ia_addr.sin6_addr.s6_addr32[1] = 0; + + switch (type) { + case IN6_IFT_LOOP: + ia->ia_addr.sin6_addr.s6_addr32[2] = 0; + ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); + break; + case IN6_IFT_802: + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + rtflag = RTF_CLONING; + /* fall through */ + case IN6_IFT_P2P802: + if (laddr == NULL) + break; + /* XXX use laddrlen */ + if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8], + laddr, 6) != 0) { + break; + } + /* invert u bit to convert EUI64 to RFC2373 interface ID. */ + ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02; + if (found_first_ifid == 0) { + if (in6_ifattach_getifid(ifp) == 0) + in6_ifattach_p2p(); + } + break; + case IN6_IFT_P2P: + bcopy((caddr_t)first_ifid, + (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8], + IFID_LEN); + break; + case IN6_IFT_ARCNET: + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + rtflag = RTF_CLONING; + if (laddr == NULL) + break; + + /* make non-global IF id out of link-level address */ + bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7); + ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr; + } + + ia->ia_ifa.ifa_metric = ifp->if_metric; + + if (ifp->if_ioctl != NULL) { + int s; + int error; + + /* + * give the interface a chance to initialize, in case this + * is the first address to be added. + */ + s = splimp(); + error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); + splx(s); + + if (error) { + switch (error) { + case EAFNOSUPPORT: + printf("%s: IPv6 not supported\n", + if_name(ifp)); + break; + default: + printf("%s: SIOCSIFADDR error %d\n", + if_name(ifp), error); + break; + } + + /* undo changes */ + TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + if (oia) + oia->ia_next = ia->ia_next; + else + in6_ifaddr = ia->ia_next; + free(ia, M_IFADDR); + return; + } + } + + /* add route to the interface. */ + rtrequest(RTM_ADD, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_prefixmask, + RTF_UP|rtflag, + (struct rtentry **)0); + ia->ia_flags |= IFA_ROUTE; + + if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) { + /* + * route local address to loopback + */ + bzero(&gate, sizeof(gate)); + gate.sin6_len = sizeof(struct sockaddr_in6); + gate.sin6_family = AF_INET6; + gate.sin6_addr = in6addr_loopback; + bzero(&mask, sizeof(mask)); + mask.sin6_len = sizeof(struct sockaddr_in6); + mask.sin6_family = AF_INET6; + mask.sin6_addr = in6mask64; + rtrequest(RTM_ADD, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&gate, + (struct sockaddr *)&mask, + RTF_UP|RTF_HOST, + (struct rtentry **)0); + } + + /* + * loopback address + */ + ib = (struct in6_ifaddr *)NULL; + if (type == IN6_IFT_LOOP) { + ib = (struct in6_ifaddr *) + malloc(sizeof(*ib), M_IFADDR, M_WAITOK); + bzero((caddr_t)ib, sizeof(*ib)); + ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr; + ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr; + ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask; + ib->ia_ifp = ifp; + + ia->ia_next = ib; + TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib, + ifa_list); + + ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ib->ia_prefixmask.sin6_family = AF_INET6; + ib->ia_prefixmask.sin6_addr = in6mask128; + ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6); + ib->ia_addr.sin6_family = AF_INET6; + ib->ia_addr.sin6_addr = in6addr_loopback; + ib->ia_ifa.ifa_metric = ifp->if_metric; + + rtrequest(RTM_ADD, + (struct sockaddr *)&ib->ia_addr, + (struct sockaddr *)&ib->ia_addr, + (struct sockaddr *)&ib->ia_prefixmask, + RTF_UP|RTF_HOST, + (struct rtentry **)0); + + ib->ia_flags |= IFA_ROUTE; + } + + /* + * join multicast + */ + if (ifp->if_flags & IFF_MULTICAST) { + int error; /* not used */ + + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); + mltmask.sin6_family = AF_INET6; + mltmask.sin6_addr = in6mask32; + + /* + * join link-local all-nodes address + */ + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_addr = in6addr_linklocal_allnodes; + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + rtrequest(RTM_ADD, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, + RTF_UP|RTF_CLONING, /* xxx */ + (struct rtentry **)0); + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + + if (type == IN6_IFT_LOOP) { + /* + * join node-local all-nodes address + */ + mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + rtrequest(RTM_ADD, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ib->ia_addr, + (struct sockaddr *)&mltmask, + RTF_UP, + (struct rtentry **)0); + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + } else { + /* + * join solicited multicast address + */ + bzero(&llsol, sizeof(llsol)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + (void)in6_addmulti(&llsol, ifp, &error); + } + } + + /* update dynamically. */ + if (in6_maxmtu < ifp->if_mtu) + in6_maxmtu = ifp->if_mtu; + + if (in6_ifstat[ifp->if_index] == NULL) { + in6_ifstat[ifp->if_index] = (struct in6_ifstat *) + malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK); + bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat)); + } + if (icmp6_ifstat[ifp->if_index] == NULL) { + icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *) + malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK); + bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat)); + } + + /* initialize NDP variables */ + nd6_ifattach(ifp); + + /* mark the address TENTATIVE, if needed. */ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + ia->ia6_flags |= IN6_IFF_TENTATIVE; + /* nd6_dad_start() will be called in in6_if_up */ + break; +#ifdef IFT_DUMMY + case IFT_DUMMY: +#endif + case IFT_GIF: /*XXX*/ + case IFT_LOOP: + case IFT_FAITH: + default: + break; + } + + return; +} + +void +in6_ifdetach(ifp) + struct ifnet *ifp; +{ + struct in6_ifaddr *ia, *oia; + struct ifaddr *ifa; + struct rtentry *rt; + short rtflags; + + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6 + || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) { + continue; + } + + ia = (struct in6_ifaddr *)ifa; + + /* remove from the routing table */ + if ((ia->ia_flags & IFA_ROUTE) + && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { + rtflags = rt->rt_flags; + rtfree(rt); + rtrequest(RTM_DELETE, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_prefixmask, + rtflags, (struct rtentry **)0); + } + + /* remove from the linked list */ + TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + + /* also remove from the IPv6 address chain(itojun&jinmei) */ + oia = ia; + if (oia == (ia = in6_ifaddr)) + in6_ifaddr = ia->ia_next; + else { + while (ia->ia_next && (ia->ia_next != oia)) + ia = ia->ia_next; + if (ia->ia_next) + ia->ia_next = oia->ia_next; +#ifdef DEBUG + else + printf("%s: didn't unlink in6ifaddr from " + "list\n", if_name(ifp)); +#endif + } + + free(ia, M_IFADDR); + } +} diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h new file mode 100644 index 0000000..03dbac7 --- /dev/null +++ b/sys/netinet6/in6_ifattach.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETINET6_IN6_IFATTACH_H_ +#define _NETINET6_IN6_IFATTACH_H_ + +#ifdef _KERNEL +extern int found_first_ifid; + +int in6_ifattach_getifid __P((struct ifnet *)); +void in6_ifattach_p2p __P((void)); +void in6_ifattach __P((struct ifnet *, u_int, caddr_t, int)); +void in6_ifdetach __P((struct ifnet *)); +#endif /* _KERNEL */ + +#define IN6_IFT_LOOP 1 +#define IN6_IFT_P2P 2 +#define IN6_IFT_802 3 +#define IN6_IFT_P2P802 4 +#define IN6_IFT_ARCNET 5 + +#endif /* _NETINET6_IN6_IFATTACH_H_ */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c new file mode 100644 index 0000000..884410a --- /dev/null +++ b/sys/netinet6/in6_pcb.c @@ -0,0 +1,1131 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 + * $FreeBSD$ + */ + +#include "opt_inet.h" +#include "opt_ipsec.h" +#include "opt_key.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* #include "faith.h" */ + +#ifdef IPSEC +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#include +#ifdef KEY_DEBUG +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#else +#define DPRINTF(lev,arg) +#define DDO(lev, stmt) +#define DP(x, y, z) +#endif /* KEY_DEBUG */ +#endif /* IPSEC */ + +struct in6_addr zeroin6_addr; + +int +in6_pcbbind(inp, nam, p) + register struct inpcb *inp; + struct sockaddr *nam; + struct proc *p; +{ + struct socket *so = inp->inp_socket; + unsigned short *lastport; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; + struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; + u_short lport = 0; + int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); + int error; + + if (!in6_ifaddr) /* XXX broken! */ + return (EADDRNOTAVAIL); + if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) + return(EINVAL); + if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) + wild = 1; + if (nam) { + sin6 = (struct sockaddr_in6 *)nam; + if (nam->sa_len != sizeof(*sin6)) + return(EINVAL); + /* + * family check. + */ + if (nam->sa_family != AF_INET6) + return(EAFNOSUPPORT); + + /* + * If the scope of the destination is link-local, embed the + * interface index in the address. + */ + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { + /* XXX boundary check is assumed to be already done. */ + /* XXX sin6_scope_id is weaker than advanced-api. */ + struct in6_pktinfo *pi; + if (inp->in6p_outputopts && + (pi = inp->in6p_outputopts->ip6po_pktinfo) && + pi->ipi6_ifindex) { + sin6->sin6_addr.s6_addr16[1] + = htons(pi->ipi6_ifindex); + } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) + && inp->in6p_moptions + && inp->in6p_moptions->im6o_multicast_ifp) { + sin6->sin6_addr.s6_addr16[1] = + htons(inp->in6p_moptions->im6o_multicast_ifp->if_index); + } else if (sin6->sin6_scope_id) { + /* boundary check */ + if (sin6->sin6_scope_id < 0 + || if_index < sin6->sin6_scope_id) { + return ENXIO; /* XXX EINVAL? */ + } + sin6->sin6_addr.s6_addr16[1] + = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/ + /* this must be cleared for ifa_ifwithaddr() */ + sin6->sin6_scope_id = 0; + } + } + + lport = sin6->sin6_port; + if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { + /* + * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; + * allow compepte duplication of binding if + * SO_REUSEPORT is set, or if SO_REUSEADDR is set + * and a multicast address is bound on both + * new and duplicated sockets. + */ + if (so->so_options & SO_REUSEADDR) + reuseport = SO_REUSEADDR|SO_REUSEPORT; + } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + struct ifaddr *ia = NULL; + + sin6->sin6_port = 0; /* yech... */ + if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0) + return(EADDRNOTAVAIL); + + /* + * XXX: bind to an anycast address might accidentally + * cause sending a packet with anycast source address. + */ + if (ia && + ((struct in6_ifaddr *)ia)->ia6_flags & + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| + IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + return(EADDRNOTAVAIL); + } + } + if (lport) { + struct inpcb *t; + + /* GROSS */ + if (ntohs(lport) < IPV6PORT_RESERVED && p && + suser_xxx(0, p, PRISON_ROOT)) + return(EACCES); + if (so->so_cred->cr_uid != 0 && + !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { + t = in6_pcblookup_local(inp->inp_pcbinfo, + &sin6->sin6_addr, lport, + INPLOOKUP_WILDCARD); + if (t && + (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || + !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || + (t->inp_socket->so_options & + SO_REUSEPORT) == 0) && + (so->so_cred->cr_uid != + t->inp_socket->so_cred->cr_uid)) + return (EADDRINUSE); + } + t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, + lport, wild); + if (t && (reuseport & t->inp_socket->so_options) == 0) + return(EADDRINUSE); + } + inp->in6p_laddr = sin6->sin6_addr; + } + if (lport == 0) { + ushort first, last; + int count; + + inp->inp_flags |= INP_ANONPORT; + + if (inp->inp_flags & INP_HIGHPORT) { + first = ipport_hifirstauto; /* sysctl */ + last = ipport_hilastauto; + lastport = &pcbinfo->lasthi; + } else if (inp->inp_flags & INP_LOWPORT) { + if (p && (error = suser_xxx(0, p, PRISON_ROOT))) + return error; + first = ipport_lowfirstauto; /* 1023 */ + last = ipport_lowlastauto; /* 600 */ + lastport = &pcbinfo->lastlow; + } else { + first = ipport_firstauto; /* sysctl */ + last = ipport_lastauto; + lastport = &pcbinfo->lastport; + } + /* + * Simple check to ensure all ports are not used up causing + * a deadlock here. + * + * We split the two cases (up and down) so that the direction + * is not being tested on each round of the loop. + */ + if (first > last) { + /* + * counting down + */ + count = first - last; + + do { + if (count-- < 0) { /* completely used? */ + /* + * Undo any address bind that may have + * occurred above. + */ + inp->in6p_laddr = in6addr_any; + return (EAGAIN); + } + --*lastport; + if (*lastport > first || *lastport < last) + *lastport = first; + lport = htons(*lastport); + } while (in6_pcblookup_local(pcbinfo, + &inp->in6p_laddr, lport, wild)); + } else { + /* + * counting up + */ + count = last - first; + + do { + if (count-- < 0) { /* completely used? */ + /* + * Undo any address bind that may have + * occurred above. + */ + inp->in6p_laddr = in6addr_any; + return (EAGAIN); + } + ++*lastport; + if (*lastport < first || *lastport > last) + *lastport = first; + lport = htons(*lastport); + } while (in6_pcblookup_local(pcbinfo, + &inp->in6p_laddr, lport, wild)); + } + } + inp->inp_lport = lport; + if (in_pcbinshash(inp) != 0) { + inp->in6p_laddr = in6addr_any; + inp->inp_lport = 0; + return (EAGAIN); + } + inp->in6p_flowinfo = sin6 ? sin6->sin6_flowinfo : 0; /*XXX*/ + return(0); +} + +/* + * Transform old in6_pcbconnect() into an inner subroutine for new + * in6_pcbconnect(): Do some validity-checking on the remote + * address (in mbuf 'nam') and then determine local host address + * (i.e., which interface) to use to access that remote host. + * + * This preserves definition of in6_pcbconnect(), while supporting a + * slightly different version for T/TCP. (This is more than + * a bit of a kludge, but cleaning up the internal interfaces would + * have forced minor changes in every protocol). + */ + +int +in6_pcbladdr(inp, nam, plocal_addr6) + register struct inpcb *inp; + struct sockaddr *nam; + struct in6_addr **plocal_addr6; +{ + register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; + struct in6_pktinfo *pi; + struct ifnet *ifp = NULL; + int error = 0; + + if (nam->sa_len != sizeof (*sin6)) + return (EINVAL); + if (sin6->sin6_family != AF_INET6) + return (EAFNOSUPPORT); + if (sin6->sin6_port == 0) + return (EADDRNOTAVAIL); + + /* + * If the scope of the destination is link-local, embed the interface + * index in the address. + */ + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { + /* XXX boundary check is assumed to be already done. */ + /* XXX sin6_scope_id is weaker than advanced-api. */ + if (inp->in6p_outputopts && + (pi = inp->in6p_outputopts->ip6po_pktinfo) && + pi->ipi6_ifindex) { + sin6->sin6_addr.s6_addr16[1] = htons(pi->ipi6_ifindex); + ifp = ifindex2ifnet[pi->ipi6_ifindex]; + } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && + inp->in6p_moptions && + inp->in6p_moptions->im6o_multicast_ifp) { + sin6->sin6_addr.s6_addr16[1] = + htons(inp->in6p_moptions->im6o_multicast_ifp->if_index); + ifp = ifindex2ifnet[inp->in6p_moptions->im6o_multicast_ifp->if_index]; + } else if (sin6->sin6_scope_id) { + /* boundary check */ + if (sin6->sin6_scope_id < 0 + || if_index < sin6->sin6_scope_id) { + return ENXIO; /* XXX EINVAL? */ + } + sin6->sin6_addr.s6_addr16[1] + = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/ + ifp = ifindex2ifnet[sin6->sin6_scope_id]; + } + } + + if (in6_ifaddr) { + /* + * If the destination address is UNSPECIFIED addr, + * use the loopback addr, e.g ::1. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) + sin6->sin6_addr = in6addr_loopback; + } + { + /* + * XXX: in6_selectsrc might replace the bound local address + * with the address specified by setsockopt(IPV6_PKTINFO). + * Is it the intended behavior? + */ + *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts, + inp->in6p_moptions, + &inp->in6p_route, + &inp->in6p_laddr, &error); + if (*plocal_addr6 == 0) { + if (error == 0) + error = EADDRNOTAVAIL; + return(error); + } + /* + * Don't do pcblookup call here; return interface in + * plocal_addr6 + * and exit to caller, that will do the lookup. + */ + } + + if (inp->in6p_route.ro_rt) + ifp = inp->in6p_route.ro_rt->rt_ifp; + + inp->in6p_ip6_hlim = (u_int8_t)in6_selecthlim(inp, ifp); + + return(0); +} + +/* + * Outer subroutine: + * Connect from a socket to a specified address. + * Both address and port must be specified in argument sin. + * If don't have a local address for this socket yet, + * then pick one. + */ +int +in6_pcbconnect(inp, nam, p) + register struct inpcb *inp; + struct sockaddr *nam; + struct proc *p; +{ + struct in6_addr *addr6; + register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; + int error; + + /* + * Call inner routine, to assign local interface address. + */ + if ((error = in6_pcbladdr(inp, nam, &addr6)) != 0) + return(error); + + if (in6_pcblookup_hash(inp->inp_pcbinfo, &sin6->sin6_addr, + sin6->sin6_port, + IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) + ? addr6 : &inp->in6p_laddr, + inp->inp_lport, 0, NULL) != NULL) { + return (EADDRINUSE); + } + if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { + if (inp->inp_lport == 0) { + error = in6_pcbbind(inp, (struct sockaddr *)0, p); + if (error) + return (error); + } + inp->in6p_laddr = *addr6; + } + inp->in6p_faddr = sin6->sin6_addr; + inp->inp_fport = sin6->sin6_port; + /* + * xxx kazu flowlabel is necessary for connect? + * but if this line is missing, the garbage value remains. + */ + inp->in6p_flowinfo = sin6->sin6_flowinfo; + + in_pcbrehash(inp); + return (0); +} + +/* + * Return an IPv6 address, which is the most appropriate for given + * destination and user specified options. + * If necessary, this function lookups the routing table and return + * an entry to the caller for later use. + */ +struct in6_addr * +in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) + struct sockaddr_in6 *dstsock; + struct ip6_pktopts *opts; + struct ip6_moptions *mopts; + struct route_in6 *ro; + struct in6_addr *laddr; + int *errorp; +{ + struct in6_addr *dst; + struct in6_ifaddr *ia6 = 0; + struct in6_pktinfo *pi = NULL; + + dst = &dstsock->sin6_addr; + *errorp = 0; + + /* + * If the source address is explicitly specified by the caller, + * use it. + */ + if (opts && (pi = opts->ip6po_pktinfo) && + !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) + return(&pi->ipi6_addr); + + /* + * If the source address is not specified but the socket(if any) + * is already bound, use the bound address. + */ + if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) + return(laddr); + + /* + * If the caller doesn't specify the source address but + * the outgoing interface, use an address associated with + * the interface. + */ + if (pi && pi->ipi6_ifindex) { + /* XXX boundary check is assumed to be already done. */ + ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex], + dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + /* + * If the destination address is a link-local unicast address or + * a multicast address, and if the outgoing interface is specified + * by the sin6_scope_id filed, use an address associated with the + * interface. + * XXX: We're now trying to define more specific semantics of + * sin6_scope_id field, so this part will be rewritten in + * the near future. + */ + if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) && + dstsock->sin6_scope_id) { + /* + * I'm not sure if boundary check for scope_id is done + * somewhere... + */ + if (dstsock->sin6_scope_id < 0 || + if_index < dstsock->sin6_scope_id) { + *errorp = ENXIO; /* XXX: better error? */ + return(0); + } + ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id], + dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + /* + * If the destination address is a multicast address and + * the outgoing interface for the address is specified + * by the caller, use an address associated with the interface. + * There is a sanity check here; if the destination has node-local + * scope, the outgoing interfacde should be a loopback address. + * Even if the outgoing interface is not specified, we also + * choose a loopback interface as the outgoing interface. + */ + if (IN6_IS_ADDR_MULTICAST(dst)) { + struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL; + + if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) { + ifp = &loif[0]; + } + + if (ifp) { + ia6 = in6_ifawithscope(ifp, dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&ia6->ia_addr.sin6_addr); + } + } + + /* + * If the next hop address for the packet is specified + * by caller, use an address associated with the route + * to the next hop. + */ + { + struct sockaddr_in6 *sin6_next; + struct rtentry *rt; + + if (opts && opts->ip6po_nexthop) { + sin6_next = satosin6(opts->ip6po_nexthop); + rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL); + if (rt) { + ia6 = in6_ifawithscope(rt->rt_ifp, dst); + if (ia6 == 0) + ia6 = ifatoia6(rt->rt_ifa); + } + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + } + + /* + * If route is known or can be allocated now, + * our src addr is taken from the i/f, else punt. + */ + if (ro) { + if (ro->ro_rt && + !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)0; + } + if (ro->ro_rt == (struct rtentry *)0 || + ro->ro_rt->rt_ifp == (struct ifnet *)0) { + /* No route yet, so try to acquire one */ + bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); + ro->ro_dst.sin6_family = AF_INET6; + ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6); + ro->ro_dst.sin6_addr = *dst; + if (IN6_IS_ADDR_MULTICAST(dst)) { + ro->ro_rt = rtalloc1(&((struct route *)ro) + ->ro_dst, 0, 0UL); + } else { + rtcalloc((struct route *)ro); + } + } + + /* + * in_pcbconnect() checks out IFF_LOOPBACK to skip using + * the address. But we don't know why it does so. + * It is necessary to ensure the scope even for lo0 + * so doesn't check out IFF_LOOPBACK. + */ + + if (ro->ro_rt) { + ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst); + if (ia6 == 0) /* xxx scope error ?*/ + ia6 = ifatoia6(ro->ro_rt->rt_ifa); + } + if (ia6 == 0) { + *errorp = EHOSTUNREACH; /* no route */ + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + *errorp = EADDRNOTAVAIL; + return(0); +} + +/* + * Default hop limit selection. The precedence is as follows: + * 1. Hoplimit valued specified via ioctl. + * 2. (If the outgoing interface is detected) the current + * hop limit of the interface specified by router advertisement. + * 3. The system default hoplimit. +*/ +int +in6_selecthlim(in6p, ifp) + struct in6pcb *in6p; + struct ifnet *ifp; +{ + if (in6p && in6p->in6p_hops >= 0) + return(in6p->in6p_hops); + else if (ifp) + return(nd_ifinfo[ifp->if_index].chlim); + else + return(ip6_defhlim); +} + +void +in6_pcbdisconnect(inp) + struct inpcb *inp; +{ + bzero((caddr_t)&inp->in6p_faddr, sizeof(inp->in6p_faddr)); + inp->inp_fport = 0; + in_pcbrehash(inp); + if (inp->inp_socket->so_state & SS_NOFDREF) + in6_pcbdetach(inp); +} + +void +in6_pcbdetach(inp) + struct inpcb *inp; +{ + struct socket *so = inp->inp_socket; + struct inpcbinfo *ipi = inp->inp_pcbinfo; + +#ifdef IPSEC + if (sotoinpcb(so) != 0) + key_freeso(so); + ipsec6_delete_pcbpolicy(inp); +#endif /* IPSEC */ + inp->inp_gencnt = ++ipi->ipi_gencnt; + in_pcbremlists(inp); + sotoinpcb(so) = 0; + sofree(so); + if (inp->in6p_options) + m_freem(inp->in6p_options); + if (inp->in6p_outputopts) { + if (inp->in6p_outputopts->ip6po_rthdr && + inp->in6p_outputopts->ip6po_route.ro_rt) + RTFREE(inp->in6p_outputopts->ip6po_route.ro_rt); + if (inp->in6p_outputopts->ip6po_m) + (void)m_free(inp->in6p_outputopts->ip6po_m); + free(inp->in6p_outputopts, M_IP6OPT); + } + if (inp->in6p_route.ro_rt) + rtfree(inp->in6p_route.ro_rt); + ip6_freemoptions(inp->in6p_moptions); + inp->inp_vflag = 0; + zfreei(ipi->ipi_zone, inp); +} + +/* + * The calling convention of in6_setsockaddr() and in6_setpeeraddr() was + * modified to match the pru_sockaddr() and pru_peeraddr() entry points + * in struct pr_usrreqs, so that protocols can just reference then directly + * without the need for a wrapper function. The socket must have a valid + * (i.e., non-nil) PCB, but it should be impossible to get an invalid one + * except through a kernel programming error, so it is acceptable to panic + * (or in this case trap) if the PCB is invalid. (Actually, we don't trap + * because there actually /is/ a programming error somewhere... XXX) + */ +int +in6_setsockaddr(so, nam) + struct socket *so; + struct sockaddr **nam; +{ + int s; + register struct inpcb *inp; + register struct sockaddr_in6 *sin6; + + /* + * Do the malloc first in case it blocks. + */ + MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK); + bzero(sin6, sizeof *sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + + s = splnet(); + inp = sotoinpcb(so); + if (!inp) { + splx(s); + free(sin6, M_SONAME); + return EINVAL; + } + sin6->sin6_port = inp->inp_lport; + sin6->sin6_addr = inp->in6p_laddr; + splx(s); + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); + else + sin6->sin6_scope_id = 0; /*XXX*/ + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_addr.s6_addr16[1] = 0; + + *nam = (struct sockaddr *)sin6; + return 0; +} + +int +in6_setpeeraddr(so, nam) + struct socket *so; + struct sockaddr **nam; +{ + int s; + struct inpcb *inp; + register struct sockaddr_in6 *sin6; + + /* + * Do the malloc first in case it blocks. + */ + MALLOC(sin6, struct sockaddr_in6 *, sizeof(*sin6), M_SONAME, M_WAITOK); + bzero((caddr_t)sin6, sizeof (*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + + s = splnet(); + inp = sotoinpcb(so); + if (!inp) { + splx(s); + free(sin6, M_SONAME); + return EINVAL; + } + sin6->sin6_port = inp->inp_fport; + sin6->sin6_addr = inp->in6p_faddr; + splx(s); + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); + else + sin6->sin6_scope_id = 0; /*XXX*/ + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_addr.s6_addr16[1] = 0; + + *nam = (struct sockaddr *)sin6; + return 0; +} + +int +in6_mapped_sockaddr(struct socket *so, struct sockaddr **nam) +{ + struct inpcb *inp = sotoinpcb(so); + int error; + + if (inp == NULL) + return EINVAL; + if (inp->inp_vflag & INP_IPV4) { + error = in_setsockaddr(so, nam); + if (error == NULL) + in6_sin_2_v4mapsin6_in_sock(nam); + } else + error = in6_setsockaddr(so, nam); + + return error; +} + +int +in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam) +{ + struct inpcb *inp = sotoinpcb(so); + int error; + + if (inp == NULL) + return EINVAL; + if (inp->inp_vflag & INP_IPV4) { + error = in_setpeeraddr(so, nam); + if (error == NULL) + in6_sin_2_v4mapsin6_in_sock(nam); + } else + error = in6_setpeeraddr(so, nam); + + return error; +} + +/* + * 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 splnet. + */ +void +in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) + struct inpcbhead *head; + struct sockaddr *dst; + u_int fport_arg, lport_arg; + struct in6_addr *laddr6; + int cmd; + void (*notify) __P((struct inpcb *, int)); +{ + struct inpcb *inp, *oinp; + struct in6_addr faddr6; + u_short fport = fport_arg, lport = lport_arg; + int errno, s; + + if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6) + return; + faddr6 = ((struct sockaddr_in6 *)dst)->sin6_addr; + if (IN6_IS_ADDR_UNSPECIFIED(&faddr6)) + return; + + /* + * Redirects go to all references to the destination, + * and use in_rtchange to invalidate the route cache. + * Dead host indications: notify all references to the destination. + * 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((caddr_t)laddr6, sizeof(*laddr6)); + if (cmd != PRC_HOSTDEAD) + notify = in6_rtchange; + } + errno = inet6ctlerrmap[cmd]; + s = splnet(); + for (inp = LIST_FIRST(head); inp != NULL;) { + if ((inp->inp_vflag & INP_IPV6) == NULL) { + inp = LIST_NEXT(inp, inp_list); + continue; + } + if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &faddr6) || + inp->inp_socket == 0 || + (lport && inp->inp_lport != lport) || + (!IN6_IS_ADDR_UNSPECIFIED(laddr6) && + !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr6)) || + (fport && inp->inp_fport != fport)) { + inp = LIST_NEXT(inp, inp_list); + continue; + } + oinp = inp; + inp = LIST_NEXT(inp, inp_list); + if (notify) + (*notify)(oinp, errno); + } + splx(s); +} + +/* + * Lookup a PCB based on the local address and port. + */ +struct inpcb * +in6_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) + struct inpcbinfo *pcbinfo; + struct in6_addr *laddr; + u_int lport_arg; + int wild_okay; +{ + register struct inpcb *inp; + int matchwild = 3, wildcard; + u_short lport = lport_arg; + + if (!wild_okay) { + struct inpcbhead *head; + /* + * Look for an unconnected (wildcard foreign addr) PCB that + * matches the local address and port we're looking for. + */ + head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, + pcbinfo->hashmask)]; + LIST_FOREACH(inp, head, inp_hash) { + if ((inp->inp_vflag & INP_IPV6) == NULL) + continue; + if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && + IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) && + inp->inp_lport == lport) { + /* + * Found. + */ + return (inp); + } + } + /* + * Not found. + */ + return (NULL); + } else { + struct inpcbporthead *porthash; + struct inpcbport *phd; + struct inpcb *match = NULL; + /* + * Best fit PCB lookup. + * + * First see if this local port is in use by looking on the + * port hash list. + */ + porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, + pcbinfo->porthashmask)]; + LIST_FOREACH(phd, porthash, phd_hash) { + if (phd->phd_port == lport) + break; + } + if (phd != NULL) { + /* + * Port is in use by one or more PCBs. Look for best + * fit. + */ + LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { + wildcard = 0; + if ((inp->inp_vflag & INP_IPV6) == NULL) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) + wildcard++; + if (!IN6_IS_ADDR_UNSPECIFIED( + &inp->in6p_laddr)) { + if (IN6_IS_ADDR_UNSPECIFIED(laddr)) + wildcard++; + else if (!IN6_ARE_ADDR_EQUAL( + &inp->in6p_laddr, laddr)) + continue; + } else { + if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) + wildcard++; + } + if (wildcard < matchwild) { + match = inp; + matchwild = wildcard; + if (matchwild == 0) { + break; + } + } + } + } + return (match); + } +} + +/* + * Check for alternatives when higher level complains + * about service problems. For now, invalidate cached + * routing information. If the route was created dynamically + * (by a redirect), time to try a default gateway again. + */ +void +in6_losing(in6p) + struct inpcb *in6p; +{ + struct rtentry *rt; + struct rt_addrinfo info; + + if ((rt = in6p->in6p_route.ro_rt) != NULL) { + in6p->in6p_route.ro_rt = 0; + bzero((caddr_t)&info, sizeof(info)); + info.rti_info[RTAX_DST] = + (struct sockaddr *)&in6p->in6p_route.ro_dst; + info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; + info.rti_info[RTAX_NETMASK] = rt_mask(rt); + rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); + if (rt->rt_flags & RTF_DYNAMIC) + (void)rtrequest(RTM_DELETE, rt_key(rt), + rt->rt_gateway, rt_mask(rt), rt->rt_flags, + (struct rtentry **)0); + else + /* + * A new route can be allocated + * the next time output is attempted. + */ + rtfree(rt); + } +} + +/* + * After a routing change, flush old routing + * and allocate a (hopefully) better one. + */ +void +in6_rtchange(inp, errno) + struct inpcb *inp; + int errno; +{ + if (inp->in6p_route.ro_rt) { + rtfree(inp->in6p_route.ro_rt); + inp->in6p_route.ro_rt = 0; + /* + * A new route can be allocated the next time + * output is attempted. + */ + } +} + +/* + * Lookup PCB in hash list. + */ +struct inpcb * +in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp) + struct inpcbinfo *pcbinfo; + struct in6_addr *faddr, *laddr; + u_int fport_arg, lport_arg; + int wildcard; + struct ifnet *ifp; +{ + struct inpcbhead *head; + register struct inpcb *inp; + u_short fport = fport_arg, lport = lport_arg; + + /* + * First look for an exact match. + */ + head = &pcbinfo->hashbase[INP_PCBHASH(faddr->s6_addr32[3] /* XXX */, + lport, fport, + pcbinfo->hashmask)]; + for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { + if ((inp->inp_vflag & INP_IPV6) == NULL) + continue; + if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) && + IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) && + inp->inp_fport == fport && + inp->inp_lport == lport) { + /* + * Found. + */ + return (inp); + } + } + if (wildcard) { + struct inpcb *local_wild = NULL; + + head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, + pcbinfo->hashmask)]; + for (inp = head->lh_first; inp != NULL; + inp = inp->inp_hash.le_next) { + if ((inp->inp_vflag & INP_IPV6) == NULL) + continue; + if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && + inp->inp_lport == lport) { +#if defined(NFAITH) && NFAITH > 0 + if (ifp && ifp->if_type == IFT_FAITH && + (inp->inp_flags & INP_FAITH) == 0) + continue; +#endif + if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, + laddr)) + return (inp); + else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) + local_wild = inp; + } + } + return (local_wild); + } + + /* + * Not found. + */ + return (NULL); +} + +void +init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m) +{ + struct ip6_hdr *ip; + + ip = mtod(m, struct ip6_hdr *); + bzero(sin6, sizeof(*sin6)); + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_addr = ip->ip6_src; + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = + (m->m_pkthdr.rcvif && IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + ? m->m_pkthdr.rcvif->if_index : 0; + + return; +} diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h new file mode 100644 index 0000000..d962b21 --- /dev/null +++ b/sys/netinet6/in6_pcb.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_pcb.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET6_IN6_PCB_H_ +#define _NETINET6_IN6_PCB_H_ + +#ifdef KERNEL +#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) +#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) +#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) + +void in6_losing __P((struct inpcb *)); +int in6_pcballoc __P((struct socket *, struct inpcbinfo *, struct proc *)); +int in6_pcbbind __P((struct inpcb *, struct sockaddr *, struct proc *)); +int in6_pcbconnect __P((struct inpcb *, struct sockaddr *, struct proc *)); +void in6_pcbdetach __P((struct inpcb *)); +void in6_pcbdisconnect __P((struct inpcb *)); +int in6_pcbladdr __P((struct inpcb *, struct sockaddr *, + struct in6_addr **)); +struct inpcb * + in6_pcblookup_local __P((struct inpcbinfo *, + struct in6_addr *, u_int, int)); +struct inpcb * + in6_pcblookup_hash __P((struct inpcbinfo *, + struct in6_addr *, u_int, struct in6_addr *, + u_int, int, struct ifnet *)); +void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *, + u_int, struct in6_addr *, u_int, int, + void (*)(struct inpcb *, int))); +void in6_rtchange __P((struct inpcb *, int)); +int in6_setpeeraddr __P((struct socket *so, struct sockaddr **nam)); +int in6_setsockaddr __P((struct socket *so, struct sockaddr **nam)); +int in6_mapped_sockaddr __P((struct socket *so, struct sockaddr **nam)); +int in6_mapped_peeraddr __P((struct socket *so, struct sockaddr **nam)); +struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, + struct ip6_pktopts *, + struct ip6_moptions *, + struct route_in6 *, + struct in6_addr *, int *)); +int in6_selecthlim __P((struct inpcb *, struct ifnet *)); + +void init_sin6 __P((struct sockaddr_in6 *sin6, struct mbuf *m)); +#endif /* KERNEL */ + +#endif /* !_NETINET6_IN6_PCB_H_ */ diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c new file mode 100644 index 0000000..8da07c4 --- /dev/null +++ b/sys/netinet6/in6_prefix.c @@ -0,0 +1,1106 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in.c 8.2 (Berkeley) 11/15/93 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +static MALLOC_DEFINE(M_IP6RR, "ip6rr", "IPv6 Router Renumbering Prefix"); +static MALLOC_DEFINE(M_RR_ADDR, "rp_addr", "IPv6 Router Renumbering Ifid"); + +struct rr_prhead rr_prefix; + +#include + +static int create_ra_entry __P((struct rp_addr **rapp)); +static int add_each_prefix __P((struct socket *so, + struct rr_prefix *rpp)); +static void free_rp_entries __P((struct rr_prefix *rpp)); +static int link_stray_ia6s __P((struct rr_prefix *rpp)); + +/* + * Copy bits from src to tgt, from off bit for len bits. + * Caller must specify collect tgtsize and srcsize. + */ +static void +bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize, + u_int off, u_int len) +{ + char *sp, *tp; + + /* arg values check */ + if (srcsize < off || srcsize < (off + len) || + tgtsize < off || tgtsize < (off + len)) { + log(LOG_ERR, + "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n" + "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off, + len); + return; + } + + /* search start point */ + for (sp = src, tp = tgt; off >= 8; sp++, tp++) + off-=8; + /* copy starting bits */ + if (off) { + char setbit; + int startbits; + + startbits = min((8 - off), len); + + for (setbit = (0x80 >> off); startbits; + setbit >>= 1, startbits--, len--) + *tp |= (setbit & *sp); + tp++; + sp++; + } + /* copy midium bits */ + for (; len >= 8; sp++, tp++) { + *tp = *sp; + len-=8; + } + /* copy ending bits */ + if (len) { + char setbit; + + for (setbit = 0x80; len; setbit >>= 1, len--) + *tp |= (setbit & *sp); + } +} + +static struct ifprefix * +in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst) +{ + struct ifprefix *ifpr; + + /* search matched prefix */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr))) + break; + } + return (ifpr); +} + +/* + * Search prefix which matches arg prefix as specified in + * draft-ietf-ipngwg-router-renum-08.txt + */ +static struct rr_prefix * +search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr) +{ + struct ifprefix *ifpr; + struct ifaddr *ifa; + struct rr_prefix *rpp; + + /* search matched prefix */ + ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen, + &ipr->ipr_prefix.sin6_addr); + if (ifpr != NULL) + return ifpr2rp(ifpr); + + /* + * search matched addr, and then search prefix + * which matches the addr + */ + + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (ipr->ipr_plen <= + in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa))) + break; + } + if (ifa == NULL) + return NULL; + + rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); + if (rpp != 0) + return rpp; + + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa), + IFPR_IN6(ifpr))) + break; + } + if (ifpr != NULL) + log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s" + "has no pointer to prefix %s", ip6_sprintf(IFA_IN6(ifa)), + ip6_sprintf(IFPR_IN6(ifpr))); + return ifpr2rp(ifpr); +} + +/* + * Search prefix which matches arg prefix as specified in + * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists. + * Return 1 if anything matched, and 0 if nothing matched. + */ +static int +mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr) +{ + struct ifprefix *ifpr; + struct ifaddr *ifa; + int matchlen, matched = 0; + + /* search matched prefixes */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr, + IFPR_IN6(ifpr)); + if (irr->irr_m_minlen > ifpr->ifpr_plen || + irr->irr_m_maxlen < ifpr->ifpr_plen || + irr->irr_m_len > matchlen) + continue; + matched = 1; + ifpr2rp(ifpr)->rp_statef_addmark = 1; + if (cmd == SIOCCIFPREFIX_IN6) + ifpr2rp(ifpr)->rp_statef_delmark = 1; + } + + /* + * search matched addr, and then search prefixes + * which matche the addr + */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) + { + struct rr_prefix *rpp; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr, + IFA_IN6(ifa)); + if (irr->irr_m_minlen > matchlen || + irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen) + continue; + rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); + if (rpp != 0) { + matched = 1; + rpp->rp_statef_addmark = 1; + if (cmd == SIOCCIFPREFIX_IN6) + rpp->rp_statef_delmark = 1; + } else + log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:" + "no back pointer to ifprefix for %s. " + "ND autoconfigured addr?", + ip6_sprintf(IFA_IN6(ifa))); + } + return matched; +} + +/* + * Mark global prefixes as to be deleted. + */ +static void +delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr) +{ + struct ifprefix *ifpr; + + /* search matched prefixes */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + /* mark delete global prefix */ + if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) == + IPV6_ADDR_SCOPE_GLOBAL) + ifpr2rp(ifpr)->rp_statef_delmark = 1; + } +} + +/* Unmark prefixes */ +static void +unmark_prefixes(struct ifnet *ifp) +{ + struct ifprefix *ifpr; + + /* unmark all prefix */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + /* unmark prefix */ + ifpr2rp(ifpr)->rp_statef_addmark = 0; + ifpr2rp(ifpr)->rp_statef_delmark = 0; + } +} + +static void +init_prefix_ltimes(struct rr_prefix *rpp) +{ + if (rpp->rp_pltime == RR_INFINITE_LIFETIME || + rpp->rp_rrf_decrprefd == 0) + rpp->rp_preferred = 0; + else + rpp->rp_preferred = time_second + rpp->rp_pltime; + if (rpp->rp_vltime == RR_INFINITE_LIFETIME || + rpp->rp_rrf_decrvalid == 0) + rpp->rp_expire = 0; + else + rpp->rp_expire = time_second + rpp->rp_vltime; +} + +static int +rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len) +{ + int ii_bytelen, ii_bitlen; + int p_bytelen, p_bitlen; + + /* sanity check */ + if (1 > ii_len || + ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */ + log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n", + ii_len); + return(0); + } + + ii_bytelen = ii_len / 8; + ii_bitlen = ii_len % 8; + + p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1; + p_bitlen = 8 - ii_bitlen; + + if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1, + ii_bytelen)) + return(0); + if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) != + ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff)) + return(0); + + return(1); +} + +static struct rp_addr * +search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid) +{ + struct rp_addr *rap; + + for (rap = rpp->rp_addrhead.lh_first; rap != NULL; + rap = rap->ra_entry.le_next) + if (rr_are_ifid_equal(ifid, &rap->ra_ifid, + (sizeof(struct in6_addr) << 3) - + rpp->rp_plen)) + break; + return rap; +} + +static int +assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) +{ + int error = 0; + struct rp_addr *rap; + int s; + + if ((error = create_ra_entry(&rap)) != 0) + return error; + + /* copy interface id part */ + bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, + (caddr_t)IA6_IN6(ia), + sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen); + /* link to ia, and put into list */ + rap->ra_addr = ia; + /* + * Can't point rp2ifpr(rpp) from ia->ia6_ifpr now, + * because rpp may be on th stack. should fix it? + */ + s = splnet(); + LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); + splx(s); + + return 0; +} + +int +in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) +{ + int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen; + struct ifprefix *ifpr; + struct rp_addr *rap; + int error = 0; + + ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia)); + if (ifpr == NULL) { + struct rr_prefix rp; + struct socket so; + int pplen = (plen == 128) ? 64 : plen; + + /* allocate a prefix for ia, with default properties */ + + /* init rp */ + bzero(&rp, sizeof(rp)); + rp.rp_type = IN6_PREFIX_RR; + rp.rp_ifp = ia->ia_ifp; + rp.rp_plen = pplen; + rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix); + rp.rp_prefix.sin6_family = AF_INET6; + bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3, + (char *)&ia->ia_addr.sin6_addr, + sizeof(ia->ia_addr.sin6_addr) << 3, + 0, pplen); + rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME; + rp.rp_raf_onlink = 1; + rp.rp_raf_auto = 1; + /* Is some FlagMasks for rrf necessary? */ + rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0; + rp.rp_origin = PR_ORIG_RR; /* can be renumbered */ + + /* create ra_entry */ + error = link_stray_ia6s(&rp); + if (error != 0) { + free_rp_entries(&rp); + return error; + } + + /* XXX: init dummy so */ + bzero(&so, sizeof(so)); + + error = add_each_prefix(&so, &rp); + + /* free each rp_addr entry */ + free_rp_entries(&rp); + + if (error != 0) + return error; + + /* search again */ + ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia)); + if (ifpr == NULL) + return 0; + } + rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia)); + if (rap != NULL) { + if (rap->ra_addr == NULL) + rap->ra_addr = ia; + else if (rap->ra_addr != ia) { + /* There may be some inconsistencies between addrs. */ + log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix" + "has already another ia %p(%s) on its ifid list", + ip6_sprintf(IA6_IN6(ia)), plen, + rap->ra_addr, + ip6_sprintf(IA6_IN6(rap->ra_addr))); + return EADDRINUSE /* XXX */; + } + ia->ia6_ifpr = ifpr; + return 0; + } + error = assigne_ra_entry(ifpr2rp(ifpr), iilen, ia); + if (error == 0) + ia->ia6_ifpr = ifpr; + return (error); +} + +void +in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia) +{ + struct rp_addr *rap; + + if (ia->ia6_ifpr == NULL) + return; + rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia)); + if (rap != NULL) { + int s = splnet(); + LIST_REMOVE(rap, ra_entry); + splx(s); + free(rap, M_RR_ADDR); + } +} + +static void +add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) +{ + struct in6_ifaddr *ia6; + struct in6_aliasreq ifra; + int error; + + /* init ifra */ + bzero(&ifra, sizeof(ifra)); + strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), sizeof(ifra.ifra_name)); + ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family = + AF_INET6; + ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len = + sizeof(ifra.ifra_addr); + /* copy prefix part */ + bit_copy((char *)&ifra.ifra_addr.sin6_addr, + sizeof(ifra.ifra_addr.sin6_addr) << 3, + (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, + 0, rpp->rp_plen); + /* copy interface id part */ + bit_copy((char *)&ifra.ifra_addr.sin6_addr, + sizeof(ifra.ifra_addr.sin6_addr) << 3, + (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, + rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen); + in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen); + /* don't care ifra_flags for now */ + + ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr); + if (ia6 != NULL) { + if (ia6->ia6_ifpr == NULL) { + /* link this addr and the prefix each other */ + rap->ra_addr = ia6; + ia6->ia6_ifpr = rp2ifpr(rpp); + return; + } + if (ia6->ia6_ifpr == rp2ifpr(rpp)) { + rap->ra_addr = ia6; + return; + } + /* + * The addr is already assigned to other + * prefix. + * There may be some inconsistencies between + * prefixes. + * e.g. overraped prefixes with common starting + * part and different plefixlen. + * Or, completely duplicated prefixes? + * log it and return. + */ + log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" + "%s/%d failed because there is already another addr %s/%d", + ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, + ip6_sprintf(IA6_IN6(ia6)), + in6_mask2len(&ia6->ia_prefixmask.sin6_addr)); + return; + } + /* propagate ANYCAST flag if it is set for ancestor addr */ + if (rap->ra_flags.anycast != 0) + ifra.ifra_flags |= IN6_IFF_ANYCAST; + error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp, + curproc); + if (error != 0) + log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" + "%s/%d failed because in6_control failed for error %d", + ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, + error); + return; + + /* + * link beween this addr and the prefix will be done + * in in6_prefix_add_ifid + */ +} + +static int +rrpr_update(struct socket *so, struct rr_prefix *new) +{ + struct rr_prefix *rpp; + struct ifprefix *ifpr; + struct rp_addr *rap; + int s; + + /* search existing prefix */ + for (ifpr = TAILQ_FIRST(&new->rp_ifp->if_prefixhead); ifpr; + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + if (ifpr->ifpr_plen == new->rp_plen && + in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new), + ifpr->ifpr_plen)) + break; + } + rpp = ifpr2rp(ifpr); + if (rpp != NULL) { + /* + * We got a prefix which we have seen in the past. + */ + /* + * If the origin of the already-installed prefix is more + * preferable than the new one, ignore installation request. + */ + if (rpp->rp_origin > new->rp_origin) + return(EPERM); + + /* update prefix information */ + rpp->rp_flags.prf_ra = new->rp_flags.prf_ra; + if (rpp->rp_origin >= PR_ORIG_RR) + rpp->rp_flags.prf_rr = new->rp_flags.prf_rr; + rpp->rp_vltime = new->rp_vltime; + rpp->rp_pltime = new->rp_pltime; + rpp->rp_expire = new->rp_expire; + rpp->rp_preferred = new->rp_preferred; + rpp->rp_statef_delmark = 0; /* cancel deletion */ + /* + * Interface id related update. + * add rp_addr entries in new into rpp, if they have not + * been already included in rpp. + */ + while (!LIST_EMPTY(&new->rp_addrhead)) + { + rap = LIST_FIRST(&new->rp_addrhead); + LIST_REMOVE(rap, ra_entry); + if (search_ifidwithprefix(rpp, &rap->ra_ifid) + != NULL) { + free(rap, M_RR_ADDR); + continue; + } + s = splnet(); + LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); + splx(s); + } + } else { + /* + * We got a fresh prefix. + */ + /* create new prefix */ + rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR, + M_NOWAIT); + if (rpp == NULL) { + log(LOG_ERR, "in6_prefix.c: rrpr_update:%d" + ": ENOBUFS for rr_prefix", __LINE__); + return(ENOBUFS); + } + /* initilization */ + *rpp = *new; + LIST_INIT(&rpp->rp_addrhead); + /* move rp_addr entries of new to rpp */ + while (!LIST_EMPTY(&new->rp_addrhead)) + { + rap = LIST_FIRST(&new->rp_addrhead); + LIST_REMOVE(rap, ra_entry); + LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); + } + + /* let rp_ifpr.ifpr_prefix point rr_prefix. */ + rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix; + /* link rr_prefix entry to if_prefixhead */ + { + struct ifnet *ifp = rpp->rp_ifp; + struct ifprefix *ifpr; + + if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead)) + != NULL) { + for ( ; TAILQ_NEXT(ifpr, ifpr_list); + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + continue; + TAILQ_NEXT(ifpr, ifpr_list) = rp2ifpr(rpp); + } else + TAILQ_FIRST(&ifp->if_prefixhead) = + rp2ifpr(rpp); + rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR; + } + /* link rr_prefix entry to rr_prefix list */ + s = splnet(); + LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry); + splx(s); + } + + if (!new->rp_raf_auto) + return 0; + + /* + * Add an address for each interface id, if it is not yet + * If it existed but not pointing to the prefix yet, + * init the prefix pointer. + */ + for (rap = rpp->rp_addrhead.lh_first; rap != NULL; + rap = rap->ra_entry.le_next) { + if (rap->ra_addr != NULL) { + if (rap->ra_addr->ia6_ifpr == NULL) + rap->ra_addr->ia6_ifpr = rp2ifpr(rpp); + continue; + } + add_each_addr(so, rpp, rap); + } + return 0; +} + +static int +add_each_prefix(struct socket *so, struct rr_prefix *rpp) +{ + init_prefix_ltimes(rpp); + return(rrpr_update(so, rpp)); +} + +static void +rp_remove(struct rr_prefix *rpp) +{ + int s; + + s = splnet(); + /* unlink rp_entry from if_prefixhead */ + { + struct ifnet *ifp = rpp->rp_ifp; + struct ifprefix *ifpr; + + if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead)) == rp2ifpr(rpp)) + TAILQ_FIRST(&ifp->if_prefixhead) = + TAILQ_NEXT(ifpr, ifpr_list); + else { + while (TAILQ_NEXT(ifpr, ifpr_list) != NULL && + (TAILQ_NEXT(ifpr, ifpr_list) != rp2ifpr(rpp))) + ifpr = TAILQ_NEXT(ifpr, ifpr_list); + if (TAILQ_NEXT(ifpr, ifpr_list)) + TAILQ_NEXT(ifpr, ifpr_list) = + TAILQ_NEXT(rp2ifpr(rpp), ifpr_list); + else + printf("Couldn't unlink rr_prefix from ifp\n"); + } + } + /* unlink rp_entry from rr_prefix list */ + LIST_REMOVE(rpp, rp_entry); + splx(s); + free(rpp, M_IP6RR); +} + +static int +create_ra_entry(struct rp_addr **rapp) +{ + *rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR, + M_NOWAIT); + if (*rapp == NULL) { + log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS" + "for rp_addr", __LINE__); + return ENOBUFS; + } + bzero(*rapp, sizeof(*(*rapp))); + + return 0; +} + +static int +init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr, + struct rr_prefix *rpp) +{ + struct rp_addr *orap; + + /* init rp */ + bzero(rpp, sizeof(*rpp)); + rpp->rp_type = IN6_PREFIX_RR; + rpp->rp_ifp = ifpr->ifpr_ifp; + rpp->rp_plen = ifpr->ifpr_plen; + rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix); + rpp->rp_prefix.sin6_family = AF_INET6; + bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, + (char *)&irr->irr_useprefix.sin6_addr, + sizeof(irr->irr_useprefix.sin6_addr) << 3, + 0, irr->irr_u_uselen); + /* copy keeplen part if necessary as necessary len */ + if (irr->irr_u_uselen < ifpr->ifpr_plen) + bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, + (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3, + irr->irr_u_uselen, + min(ifpr->ifpr_plen - irr->irr_u_uselen, + irr->irr_u_keeplen)); + for (orap = (ifpr2rp(ifpr)->rp_addrhead).lh_first; orap != NULL; + orap = orap->ra_entry.le_next) { + struct rp_addr *rap; + int error = 0; + + if ((error = create_ra_entry(&rap)) != 0) + return error; + rap->ra_ifid = orap->ra_ifid; + rap->ra_flags.anycast = (orap->ra_addr != NULL && + (orap->ra_addr->ia_flags & + IN6_IFF_ANYCAST) != 0) ? 1 : 0; + LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); + } + rpp->rp_vltime = irr->irr_vltime; + rpp->rp_pltime = irr->irr_pltime; + rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink : + ifpr2rp(ifpr)->rp_raf_onlink; + rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto : + ifpr2rp(ifpr)->rp_raf_auto; + /* Is some FlagMasks for rrf necessary? */ + rpp->rp_rrf = irr->irr_rrf; + rpp->rp_origin = irr->irr_origin; + + return 0; +} + +static void +free_rp_entries(struct rr_prefix *rpp) +{ + /* + * This func is only called with rpp on stack(not on list). + * So no splnet() here + */ + while (!LIST_EMPTY(&rpp->rp_addrhead)) + { + struct rp_addr *rap; + + rap = LIST_FIRST(&rpp->rp_addrhead); + LIST_REMOVE(rap, ra_entry); + free(rap, M_RR_ADDR); + } +} + +static int +add_useprefixes(struct socket *so, struct ifnet *ifp, + struct in6_rrenumreq *irr) +{ + struct ifprefix *ifpr, *nextifpr; + struct rr_prefix rp; + int error = 0; + + /* add prefixes to each of marked prefix */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) { + nextifpr = TAILQ_NEXT(ifpr, ifpr_list); + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + if (ifpr2rp(ifpr)->rp_statef_addmark) { + if ((error = init_newprefix(irr, ifpr, &rp)) != 0) + break; + error = add_each_prefix(so, &rp); + } + } + /* free each rp_addr entry */ + free_rp_entries(&rp); + + return error; +} + +static void +unprefer_prefix(struct rr_prefix *rpp) +{ + struct rp_addr *rap; + + for (rap = rpp->rp_addrhead.lh_first; rap != NULL; + rap = rap->ra_entry.le_next) { + if (rap->ra_addr == NULL) + continue; + rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second; + rap->ra_addr->ia6_lifetime.ia6t_pltime = 0; + } +} + +int +delete_each_prefix(struct socket *so, struct rr_prefix *rpp, u_char origin) +{ + struct in6_aliasreq ifra; + int error = 0; + + if (rpp->rp_origin > origin) + return(EPERM); + + while (rpp->rp_addrhead.lh_first != NULL) { + struct rp_addr *rap; + int s; + + s = splnet(); + rap = LIST_FIRST(&rpp->rp_addrhead); + if (rap == NULL) + break; + LIST_REMOVE(rap, ra_entry); + splx(s); + if (rap->ra_addr == NULL) { + free(rap, M_RR_ADDR); + continue; + } + rap->ra_addr->ia6_ifpr = NULL; + + bzero(&ifra, sizeof(ifra)); + strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), + sizeof(ifra.ifra_name)); + ifra.ifra_addr = rap->ra_addr->ia_addr; + ifra.ifra_dstaddr = rap->ra_addr->ia_dstaddr; + ifra.ifra_prefixmask = rap->ra_addr->ia_prefixmask; + + error = in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, + rpp->rp_ifp, curproc); + if (error != 0) + log(LOG_ERR, "in6_prefix.c: delete_each_prefix:" + "deletion of an addr %s/%d failed because" + "in6_control failed for error %d", + ip6_sprintf(&ifra.ifra_addr.sin6_addr), + rpp->rp_plen, error); + + free(rap, M_RR_ADDR); + } + rp_remove(rpp); + + return error; +} + +static void +delete_prefixes(struct socket *so, struct ifnet *ifp, u_char origin) +{ + struct ifprefix *ifpr, *nextifpr; + + /* delete prefixes marked as tobe deleted */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) { + nextifpr = TAILQ_NEXT(ifpr, ifpr_list); + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + if (ifpr2rp(ifpr)->rp_statef_delmark) + (void)delete_each_prefix(so, ifpr2rp(ifpr), origin); + } +} + +static int +link_stray_ia6s(struct rr_prefix *rpp) +{ + struct ifaddr *ifa; + + for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) + { + struct rp_addr *rap; + struct rr_prefix *orpp; + int error = 0; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa))) + continue; + + orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); + if (orpp != NULL) { + if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp), + rpp->rp_plen)) + log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:" + "addr %s/%d already linked to a prefix" + "and it matches also %s/%d", + ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen, + ip6_sprintf(RP_IN6(rpp)), + rpp->rp_plen); + continue; + } + if ((error = assigne_ra_entry(rpp, + (sizeof(rap->ra_ifid) << 3) - + rpp->rp_plen, + (struct in6_ifaddr *)ifa)) != 0) + return error; + } + return 0; +} + +int +in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, + struct ifnet *ifp) +{ + struct rr_prefix *rpp, rp_tmp; + struct rp_addr *rap; + struct in6_prefixreq *ipr = (struct in6_prefixreq *)data; + struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data; + struct ifaddr *ifa; + int error = 0; + + /* + * Failsafe for errneous address config program. + * Let's hope rrenumd don't make a mistakes. + */ + if (ipr->ipr_origin <= PR_ORIG_RA) + ipr->ipr_origin = PR_ORIG_STATIC; + + switch (cmd) { + case SIOCSGIFPREFIX_IN6: + delmark_global_prefixes(ifp, irr); + /* FALL THROUGH */ + case SIOCAIFPREFIX_IN6: + case SIOCCIFPREFIX_IN6: + /* check if preferred lifetime > valid lifetime */ + if (irr->irr_pltime > irr->irr_vltime) { + log(LOG_NOTICE, + "in6_prefix_ioctl: preferred lifetime" + "(%ld) is greater than valid lifetime(%ld)", + (u_long)irr->irr_pltime, (u_long)irr->irr_vltime); + error = EINVAL; + break; + } + if (mark_matched_prefixes(cmd, ifp, irr)) { + if (irr->irr_u_uselen != 0) + if ((error = add_useprefixes(so, ifp, irr)) + != 0) + goto failed; + if (cmd != SIOCAIFPREFIX_IN6) + delete_prefixes(so, ifp, irr->irr_origin); + } else + return (EADDRNOTAVAIL); + failed: + unmark_prefixes(ifp); + break; + case SIOCGIFPREFIX_IN6: + rpp = search_matched_prefix(ifp, ipr); + if (rpp == NULL || ifp != rpp->rp_ifp) + return (EADDRNOTAVAIL); + + ipr->ipr_origin = rpp->rp_origin; + ipr->ipr_plen = rpp->rp_plen; + ipr->ipr_vltime = rpp->rp_vltime; + ipr->ipr_pltime = rpp->rp_pltime; + ipr->ipr_flags = rpp->rp_flags; + ipr->ipr_prefix = rpp->rp_prefix; + + break; + case SIOCSIFPREFIX_IN6: + /* check if preferred lifetime > valid lifetime */ + if (ipr->ipr_pltime > ipr->ipr_vltime) { + log(LOG_NOTICE, + "in6_prefix_ioctl: preferred lifetime" + "(%ld) is greater than valid lifetime(%ld)", + (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime); + error = EINVAL; + break; + } + + /* init rp_tmp */ + bzero((caddr_t)&rp_tmp, sizeof(rp_tmp)); + rp_tmp.rp_ifp = ifp; + rp_tmp.rp_plen = ipr->ipr_plen; + rp_tmp.rp_prefix = ipr->ipr_prefix; + rp_tmp.rp_vltime = ipr->ipr_vltime; + rp_tmp.rp_pltime = ipr->ipr_pltime; + rp_tmp.rp_flags = ipr->ipr_flags; + rp_tmp.rp_origin = ipr->ipr_origin; + + /* create rp_addr entries, usually at least for lladdr */ + if ((error = link_stray_ia6s(&rp_tmp)) != 0) { + free_rp_entries(&rp_tmp); + break; + } + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + if (ifa != NULL) { + if ((error = create_ra_entry(&rap)) != 0) { + free_rp_entries(&rp_tmp); + break; + } + /* copy interface id part */ + bit_copy((caddr_t)&rap->ra_ifid, + sizeof(rap->ra_ifid) << 3, + (caddr_t)IFA_IN6(ifa), + sizeof(*IFA_IN6(ifa)) << 3, + rp_tmp.rp_plen, + (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen); + /* insert into list */ + LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry); + } + + error = add_each_prefix(so, &rp_tmp); + + /* free each rp_addr entry */ + free_rp_entries(&rp_tmp); + + break; + case SIOCDIFPREFIX_IN6: + rpp = search_matched_prefix(ifp, ipr); + if (rpp == NULL || ifp != rpp->rp_ifp) + return (EADDRNOTAVAIL); + + error = delete_each_prefix(so, rpp, ipr->ipr_origin); + break; + } + return error; +} + +void +in6_rr_timer(void *ignored_arg) +{ + int s; + struct rr_prefix *rpp; + + timeout(in6_rr_timer, (caddr_t)0, ip6_rr_prune * hz); + + s = splnet(); + /* expire */ + rpp = LIST_FIRST(&rr_prefix); + while (rpp) { + if (rpp->rp_expire && rpp->rp_expire < time_second) { + struct rr_prefix *next_rpp; + struct socket so; + + /* XXX: init dummy so */ + bzero(&so, sizeof(so)); + + next_rpp = LIST_NEXT(rpp, rp_entry); + delete_each_prefix(&so, rpp, PR_ORIG_KERNEL); + rpp = next_rpp; + continue; + } + if (rpp->rp_preferred && rpp->rp_preferred < time_second) + unprefer_prefix(rpp); + rpp = LIST_NEXT(rpp, rp_entry); + } + splx(s); +} diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h new file mode 100644 index 0000000..fa5a0e3 --- /dev/null +++ b/sys/netinet6/in6_prefix.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +struct rr_prefix { + struct ifprefix rp_ifpr; + LIST_ENTRY(rr_prefix) rp_entry; + LIST_HEAD(rp_addrhead, rp_addr) rp_addrhead; + struct sockaddr_in6 rp_prefix; /* prefix */ + u_int32_t rp_vltime; /* advertised valid lifetime */ + u_int32_t rp_pltime; /* advertised preferred lifetime */ + time_t rp_expire; /* expiration time of the prefix */ + time_t rp_preferred; /* preferred time of the prefix */ + struct in6_prflags rp_flags; + u_char rp_origin; /* from where this prefix info is obtained */ + struct rp_stateflags { + /* if some prefix should be added to this prefix */ + u_char addmark : 1; + u_char delmark : 1; /* if this prefix will be deleted */ + } rp_stateflags; +}; + +#define rp_type rp_ifpr.ifpr_type +#define rp_ifp rp_ifpr.ifpr_ifp +#define rp_plen rp_ifpr.ifpr_plen + +#define rp_raf rp_flags.prf_ra +#define rp_raf_onlink rp_flags.prf_ra.onlink +#define rp_raf_auto rp_flags.prf_ra.autonomous + +#define rp_statef_addmark rp_stateflags.addmark +#define rp_statef_delmark rp_stateflags.delmark + +#define rp_rrf rp_flags.prf_rr +#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid +#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd + +struct rp_addr { + LIST_ENTRY(rp_addr) ra_entry; + struct in6_addr ra_ifid; + struct in6_ifaddr *ra_addr; + struct ra_flags { + u_char anycast : 1; + } ra_flags; +}; + +#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr)) +#define rp2ifpr(rp) ((struct ifprefix *)(rp)) + +#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr) + +#define RR_INFINITE_LIFETIME 0xffffffff + + +LIST_HEAD(rr_prhead, rr_prefix); + +extern struct rr_prhead rr_prefix; + +void in6_rr_timer __P((void *)); +int delete_each_prefix __P((struct socket *so, struct rr_prefix *rpp, + u_char origin)); diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c new file mode 100644 index 0000000..8189d01 --- /dev/null +++ b/sys/netinet6/in6_proto.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_proto.c 8.1 (Berkeley) 6/10/93 + */ + +#include "opt_inet.h" +#include "opt_ipsec.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef IPSEC +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#include +#ifdef IPSEC_ESP +#include +#endif +#include +#endif /*IPSEC*/ + +#include + +/* #include "gif.h" */ +#if NGIF > 0 +#include +#endif + +#include + +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) + +/* + * TCP/IP protocol family: IP6, ICMP6, UDP, TCP. + */ + +extern struct domain inet6domain; +static struct pr_usrreqs nousrreqs; + +struct ip6protosw inet6sw[] = { +{ 0, &inet6domain, IPPROTO_IPV6, 0, + 0, 0, 0, 0, + 0, + ip6_init, 0, frag6_slowtimo, frag6_drain, + &nousrreqs, +}, +{ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC | PR_ADDR, + rip6_input, rip6_output, 0, rip6_ctloutput, + 0, + 0, 0, 0, 0, + &rip6_usrreqs +}, +{ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC | PR_ADDR, + icmp6_input, rip6_output, 0, rip6_ctloutput, + 0, + icmp6_init, icmp6_fasttimo, 0, 0, + &rip6_usrreqs +}, +{ SOCK_RAW, &inet6domain, IPPROTO_DSTOPTS,PR_ATOMIC|PR_ADDR, + dest6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs +}, +{ SOCK_RAW, &inet6domain, IPPROTO_ROUTING,PR_ATOMIC|PR_ADDR, + route6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs +}, +{ SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT,PR_ATOMIC|PR_ADDR, + frag6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs +}, +#ifdef IPSEC +{ SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR, + ah6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs, +}, +#ifdef IPSEC_ESP +{ SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR, + esp6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs, +}, +#endif +{ SOCK_RAW, &inet6domain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR, + ipcomp6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs, +}, +#endif /* IPSEC */ +#if NGIF > 0 +{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, + in6_gif_input,0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs +}, +#ifdef INET6 +{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, + in6_gif_input,0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs +}, +#endif /* INET6 */ +#endif /* GIF */ +/* raw wildcard */ +{ SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR, + rip6_input, rip6_output, 0, rip6_ctloutput, + 0, + 0, 0, 0, 0, + &rip6_usrreqs +}, +}; + +extern int in6_inithead __P((void **, int)); + +struct domain inet6domain = + { AF_INET6, "internet6", 0, 0, 0, + (struct protosw *)inet6sw, + (struct protosw *)&inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0, + in6_inithead, + offsetof(struct sockaddr_in6, sin6_addr) << 3, + sizeof(struct sockaddr_in6) }; + +DOMAIN_SET(inet6); + +/* + * Internet configuration info + */ +#ifndef IPV6FORWARDING +#ifdef GATEWAY6 +#define IPV6FORWARDING 1 /* forward IP6 packets not for us */ +#else +#define IPV6FORWARDING 0 /* don't forward IP6 packets not for us */ +#endif /* GATEWAY6 */ +#endif /* !IPV6FORWARDING */ + +#ifndef IPV6_SENDREDIRECTS +#define IPV6_SENDREDIRECTS 1 +#endif + +int ip6_forwarding = IPV6FORWARDING; /* act as router? */ +int ip6_sendredirects = IPV6_SENDREDIRECTS; +int ip6_defhlim = IPV6_DEFHLIM; +int ip6_defmcasthlim = IPV6_DEFAULT_MULTICAST_HOPS; +int ip6_accept_rtadv = 0; /* "IPV6FORWARDING ? 0 : 1" is dangerous */ +int ip6_maxfragpackets = 200; +int ip6_log_interval = 5; +int ip6_hdrnestlimit = 50; /* appropriate? */ +int ip6_dad_count = 1; /* DupAddrDetectionTransmits */ +u_int32_t ip6_flow_seq; +int ip6_auto_flowlabel = 1; +#if NGIF > 0 +int ip6_gif_hlim = GIF_HLIM; +#else +int ip6_gif_hlim = 0; +#endif +int ip6_use_deprecated = 1; /* allow deprecated addr (RFC2462 5.5.4) */ +int ip6_rr_prune = 5; /* router renumbering prefix + * walk list every 5 sec. */ +int ip6_mapped_addr_on = 1; + +u_int32_t ip6_id = 0UL; +int ip6_keepfaith = 0; +time_t ip6_log_time = (time_t)0L; + +/* icmp6 */ +/* + * BSDI4 defines these variables in in_proto.c... + * XXX: what if we don't define INET? Should we define pmtu6_expire + * or so? (jinmei@kame.net 19990310) + */ +int pmtu_expire = 60*10; +int pmtu_probe = 60*2; + +/* raw IP6 parameters */ +/* + * Nominal space allocated to a raw ip socket. + */ +#define RIPV6SNDQ 8192 +#define RIPV6RCVQ 8192 + +u_long rip6_sendspace = RIPV6SNDQ; +u_long rip6_recvspace = RIPV6RCVQ; + +/* ICMPV6 parameters */ +int icmp6_rediraccept = 1; /* accept and process redirects */ +int icmp6_redirtimeout = 10 * 60; /* 10 minutes */ +u_int icmp6errratelim = 1000; /* 1000usec = 1msec */ + +/* UDP on IP6 parameters */ +int udp6_sendspace = 9216; /* really max datagram size */ +int udp6_recvspace = 40 * (1024 + sizeof(struct sockaddr_in6)); + /* 40 1K datagrams */ + +/* + * sysctl related items. + */ +SYSCTL_NODE(_net, PF_INET6, inet6, CTLFLAG_RW, 0, + "Internet6 Family"); + +/* net.inet6 */ +SYSCTL_NODE(_net_inet6, IPPROTO_IPV6, ip6, CTLFLAG_RW, 0, "IP6"); +SYSCTL_NODE(_net_inet6, IPPROTO_ICMPV6, icmp6, CTLFLAG_RW, 0, "ICMP6"); +SYSCTL_NODE(_net_inet6, IPPROTO_UDP, udp6, CTLFLAG_RW, 0, "UDP6"); +SYSCTL_NODE(_net_inet6, IPPROTO_TCP, tcp6, CTLFLAG_RW, 0, "TCP6"); +#ifdef IPSEC +SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6"); +#endif /* IPSEC */ + +/* net.inet6.ip6 */ +static int +sysctl_ip6_forwarding SYSCTL_HANDLER_ARGS +{ + int error = 0; + int old_ip6_forwarding; + int changed; + + error = SYSCTL_OUT(req, arg1, sizeof(int)); + if (error || !req->newptr) + return (error); + old_ip6_forwarding = ip6_forwarding; + error = SYSCTL_IN(req, arg1, sizeof(int)); + if (error != 0) + return (error); + changed = (ip6_forwarding ? 1 : 0) ^ (old_ip6_forwarding ? 1 : 0); + if (changed == 0) + return (error); + if (ip6_forwarding != 0) { /* host becomes router */ + int s = splnet(); + struct nd_prefix *pr, *next; + + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; + if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + prelist_remove(pr); + } + splx(s); + } else { /* router becomes host */ + struct socket so; + + /* XXX: init dummy so */ + bzero(&so, sizeof(so)); + while(!LIST_EMPTY(&rr_prefix)) + delete_each_prefix(&so, LIST_FIRST(&rr_prefix), + PR_ORIG_KERNEL); + } + + return (error); +} + +SYSCTL_OID(_net_inet6_ip6, IPV6CTL_FORWARDING, forwarding, + CTLTYPE_INT|CTLFLAG_RW, &ip6_forwarding, 0, sysctl_ip6_forwarding, + "I", ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_SENDREDIRECTS, + redirect, CTLFLAG_RW, &ip6_sendredirects, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFHLIM, + hlim, CTLFLAG_RW, &ip6_defhlim, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGPACKETS, + maxfragpackets, CTLFLAG_RW, &ip6_maxfragpackets, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_ACCEPT_RTADV, + accept_rtadv, CTLFLAG_RW, &ip6_accept_rtadv, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_KEEPFAITH, + keepfaith, CTLFLAG_RW, &ip6_keepfaith, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_LOG_INTERVAL, + log_interval, CTLFLAG_RW, &ip6_log_interval, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_HDRNESTLIMIT, + hdrnestlimit, CTLFLAG_RW, &ip6_hdrnestlimit, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DAD_COUNT, + dad_count, CTLFLAG_RW, &ip6_dad_count, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_FLOWLABEL, + auto_flowlabel, CTLFLAG_RW, &ip6_auto_flowlabel, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFMCASTHLIM, + defmcasthlim, CTLFLAG_RW, &ip6_defmcasthlim, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, + gifhlim, CTLFLAG_RW, &ip6_gif_hlim, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USE_DEPRECATED, + use_deprecated, CTLFLAG_RW, &ip6_use_deprecated, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RR_PRUNE, + rr_prune, CTLFLAG_RW, &ip6_rr_prune, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAPPED_ADDR, + mapped_addr, CTLFLAG_RW, &ip6_mapped_addr_on, 0, ""); + +/* net.inet6.icmp6 */ +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRACCEPT, + rediraccept, CTLFLAG_RW, &icmp6_rediraccept, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRTIMEOUT, + redirtimeout, CTLFLAG_RW, &icmp6_redirtimeout, 0, ""); +SYSCTL_STRUCT(_net_inet6_icmp6, ICMPV6CTL_STATS, stats, CTLFLAG_RD, + &icmp6stat, icmp6stat, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRRATELIMIT, + errratelimit, CTLFLAG_RW, &icmp6errratelim, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PRUNE, + nd6_prune, CTLFLAG_RW, &nd6_prune, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DELAY, + nd6_delay, CTLFLAG_RW, &nd6_delay, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_UMAXTRIES, + nd6_umaxtries, CTLFLAG_RW, &nd6_umaxtries, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MMAXTRIES, + nd6_mmaxtries, CTLFLAG_RW, &nd6_mmaxtries, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_USELOOPBACK, + nd6_useloopback, CTLFLAG_RW, &nd6_useloopback, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PROXYALL, + nd6_proxyall, CTLFLAG_RW, &nd6_proxyall, 0, ""); diff --git a/sys/netinet6/in6_rmx.c b/sys/netinet6/in6_rmx.c new file mode 100644 index 0000000..56e9d94 --- /dev/null +++ b/sys/netinet6/in6_rmx.c @@ -0,0 +1,477 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright 1994, 1995 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: in6_rmx.c,v 1.3 1999/08/16 13:42:53 itojun Exp $ + */ + +/* + * This code does two things necessary for the enhanced TCP metrics to + * function in a useful manner: + * 1) It marks all non-host routes as `cloning', thus ensuring that + * every actual reference to such a route actually gets turned + * into a reference to a host route to the specific destination + * requested. + * 2) When such routes lose all their references, it arranges for them + * to be deleted in some random collection of circumstances, so that + * a large quantity of stale routing data is not kept in kernel memory + * indefinitely. See in6_rtqtimo() below for the exact mechanism. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +extern int in6_inithead __P((void **head, int off)); + +#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ + +/* + * Do what we need to do when inserting a route. + */ +static struct radix_node * +in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, + struct radix_node *treenodes) +{ + struct rtentry *rt = (struct rtentry *)treenodes; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt); + struct radix_node *ret; + + /* + * For IPv6, all unicast non-host routes are automatically cloning. + */ + if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + rt->rt_flags |= RTF_MULTICAST; + + if (!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) { + rt->rt_flags |= RTF_PRCLONING; + } + + /* + * A little bit of help for both IPv6 output and input: + * For local addresses, we make sure that RTF_LOCAL is set, + * with the thought that this might one day be used to speed up + * ip_input(). + * + * We also mark routes to multicast addresses as such, because + * it's easy to do and might be useful (but this is much more + * dubious since it's so easy to inspect the address). (This + * is done above.) + * + * XXX + * should elaborate the code. + */ + if (rt->rt_flags & RTF_HOST) { + if (IN6_ARE_ADDR_EQUAL(&satosin6(rt->rt_ifa->ifa_addr) + ->sin6_addr, + &sin6->sin6_addr)) { + rt->rt_flags |= RTF_LOCAL; + } + } + + /* + * We also specify a send and receive pipe size for every + * route added, to help TCP a bit. TCP doesn't actually + * want a true pipe size, which would be prohibitive in memory + * costs and is hard to compute anyway; it simply uses these + * values to size its buffers. So, we fill them in with the + * same values that TCP would have used anyway, and allow the + * installing program or the link layer to override these values + * as it sees fit. This will hopefully allow TCP more + * opportunities to save its ssthresh value. + */ + if (!rt->rt_rmx.rmx_sendpipe && !(rt->rt_rmx.rmx_locks & RTV_SPIPE)) + rt->rt_rmx.rmx_sendpipe = tcp_sendspace; + + if (!rt->rt_rmx.rmx_recvpipe && !(rt->rt_rmx.rmx_locks & RTV_RPIPE)) + rt->rt_rmx.rmx_recvpipe = tcp_recvspace; + + if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) + && rt->rt_ifp) + rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; + + ret = rn_addroute(v_arg, n_arg, head, treenodes); + if (ret == NULL && rt->rt_flags & RTF_HOST) { + struct rtentry *rt2; + /* + * We are trying to add a host route, but can't. + * Find out if it is because of an + * ARP entry and delete it if so. + */ + rt2 = rtalloc1((struct sockaddr *)sin6, 0, + RTF_CLONING | RTF_PRCLONING); + if (rt2) { + if (rt2->rt_flags & RTF_LLINFO && + rt2->rt_flags & RTF_HOST && + rt2->rt_gateway && + rt2->rt_gateway->sa_family == AF_LINK) { + rtrequest(RTM_DELETE, + (struct sockaddr *)rt_key(rt2), + rt2->rt_gateway, + rt_mask(rt2), rt2->rt_flags, 0); + ret = rn_addroute(v_arg, n_arg, head, + treenodes); + } + RTFREE(rt2); + } + } else if (ret == NULL && rt->rt_flags & RTF_CLONING) { + struct rtentry *rt2; + /* + * We are trying to add a net route, but can't. + * The following case should be allowed, so we'll make a + * special check for this: + * Two IPv6 addresses with the same prefix is assigned + * to a single interrface. + * # ifconfig if0 inet6 3ffe:0501::1 prefix 64 alias (*1) + * # ifconfig if0 inet6 3ffe:0501::2 prefix 64 alias (*2) + * In this case, (*1) and (*2) want to add the same + * net route entry, 3ffe:0501:: -> if0. + * This case should not raise an error. + */ + rt2 = rtalloc1((struct sockaddr *)sin6, 0, + RTF_CLONING | RTF_PRCLONING); + if (rt2) { + if ((rt2->rt_flags & (RTF_CLONING|RTF_HOST|RTF_GATEWAY)) + == RTF_CLONING + && rt2->rt_gateway + && rt2->rt_gateway->sa_family == AF_LINK + && rt2->rt_ifp == rt->rt_ifp) { + ret = rt2->rt_nodes; + } + RTFREE(rt2); + } + } + return ret; +} + +/* + * This code is the inverse of in6_clsroute: on first reference, if we + * were managing the route, stop doing so and set the expiration timer + * back off again. + */ +static struct radix_node * +in6_matroute(void *v_arg, struct radix_node_head *head) +{ + struct radix_node *rn = rn_match(v_arg, head); + struct rtentry *rt = (struct rtentry *)rn; + + if (rt && rt->rt_refcnt == 0) { /* this is first reference */ + if (rt->rt_flags & RTPRF_OURS) { + rt->rt_flags &= ~RTPRF_OURS; + rt->rt_rmx.rmx_expire = 0; + } + } + return rn; +} + +static int rtq_reallyold = 60*60; + /* one hour is ``really old'' */ +SYSCTL_INT(_net_inet_ip, IPCTL_RTEXPIRE, rtexpire, + CTLFLAG_RW, &rtq_reallyold , 0, ""); + +static int rtq_minreallyold = 10; + /* never automatically crank down to less */ +SYSCTL_INT(_net_inet_ip, IPCTL_RTMINEXPIRE, rtminexpire, + CTLFLAG_RW, &rtq_minreallyold , 0, ""); + +static int rtq_toomany = 128; + /* 128 cached routes is ``too many'' */ +SYSCTL_INT(_net_inet_ip, IPCTL_RTMAXCACHE, rtmaxcache, + CTLFLAG_RW, &rtq_toomany , 0, ""); + + +/* + * On last reference drop, mark the route as belong to us so that it can be + * timed out. + */ +static void +in6_clsroute(struct radix_node *rn, struct radix_node_head *head) +{ + struct rtentry *rt = (struct rtentry *)rn; + + if (!(rt->rt_flags & RTF_UP)) + return; /* prophylactic measures */ + + if ((rt->rt_flags & (RTF_LLINFO | RTF_HOST)) != RTF_HOST) + return; + + if ((rt->rt_flags & (RTF_WASCLONED | RTPRF_OURS)) + != RTF_WASCLONED) + return; + + /* + * As requested by David Greenman: + * If rtq_reallyold is 0, just delete the route without + * waiting for a timeout cycle to kill it. + */ + if (rtq_reallyold != 0) { + rt->rt_flags |= RTPRF_OURS; + rt->rt_rmx.rmx_expire = time_second + rtq_reallyold; + } else { + rtrequest(RTM_DELETE, + (struct sockaddr *)rt_key(rt), + rt->rt_gateway, rt_mask(rt), + rt->rt_flags, 0); + } +} + +struct rtqk_arg { + struct radix_node_head *rnh; + int mode; + int updating; + int draining; + int killed; + int found; + time_t nextstop; +}; + +/* + * Get rid of old routes. When draining, this deletes everything, even when + * the timeout is not expired yet. When updating, this makes sure that + * nothing has a timeout longer than the current value of rtq_reallyold. + */ +static int +in6_rtqkill(struct radix_node *rn, void *rock) +{ + struct rtqk_arg *ap = rock; + struct rtentry *rt = (struct rtentry *)rn; + int err; + + if (rt->rt_flags & RTPRF_OURS) { + ap->found++; + + if (ap->draining || rt->rt_rmx.rmx_expire <= time_second) { + if (rt->rt_refcnt > 0) + panic("rtqkill route really not free"); + + err = rtrequest(RTM_DELETE, + (struct sockaddr *)rt_key(rt), + rt->rt_gateway, rt_mask(rt), + rt->rt_flags, 0); + if (err) { + log(LOG_WARNING, "in6_rtqkill: error %d", err); + } else { + ap->killed++; + } + } else { + if (ap->updating + && (rt->rt_rmx.rmx_expire - time_second + > rtq_reallyold)) { + rt->rt_rmx.rmx_expire = time_second + + rtq_reallyold; + } + ap->nextstop = lmin(ap->nextstop, + rt->rt_rmx.rmx_expire); + } + } + + return 0; +} + +#define RTQ_TIMEOUT 60*10 /* run no less than once every ten minutes */ +static int rtq_timeout = RTQ_TIMEOUT; + +static void +in6_rtqtimo(void *rock) +{ + struct radix_node_head *rnh = rock; + struct rtqk_arg arg; + struct timeval atv; + static time_t last_adjusted_timeout = 0; + int s; + + arg.found = arg.killed = 0; + arg.rnh = rnh; + arg.nextstop = time_second + rtq_timeout; + arg.draining = arg.updating = 0; + s = splnet(); + rnh->rnh_walktree(rnh, in6_rtqkill, &arg); + splx(s); + + /* + * Attempt to be somewhat dynamic about this: + * If there are ``too many'' routes sitting around taking up space, + * then crank down the timeout, and see if we can't make some more + * go away. However, we make sure that we will never adjust more + * than once in rtq_timeout seconds, to keep from cranking down too + * hard. + */ + if ((arg.found - arg.killed > rtq_toomany) + && (time_second - last_adjusted_timeout >= rtq_timeout) + && rtq_reallyold > rtq_minreallyold) { + rtq_reallyold = 2*rtq_reallyold / 3; + if (rtq_reallyold < rtq_minreallyold) { + rtq_reallyold = rtq_minreallyold; + } + + last_adjusted_timeout = time_second; +#ifdef DIAGNOSTIC + log(LOG_DEBUG, "in6_rtqtimo: adjusted rtq_reallyold to %d", + rtq_reallyold); +#endif + arg.found = arg.killed = 0; + arg.updating = 1; + s = splnet(); + rnh->rnh_walktree(rnh, in6_rtqkill, &arg); + splx(s); + } + + atv.tv_usec = 0; + atv.tv_sec = arg.nextstop; + timeout(in6_rtqtimo, rock, tvtohz(&atv)); +} + +/* + * Age old PMTUs. + */ +struct mtuex_arg { + struct radix_node_head *rnh; + time_t nextstop; +}; + +static int +in6_mtuexpire(struct radix_node *rn, void *rock) +{ + struct rtentry *rt = (struct rtentry *)rn; + struct mtuex_arg *ap = rock; + + /* sanity */ + if (!rt) + panic("rt == NULL in in6_mtuexpire"); + + if (rt->rt_rmx.rmx_expire && !(rt->rt_flags & RTF_PROBEMTU)) { + if (rt->rt_rmx.rmx_expire <= time_second) { + rt->rt_flags |= RTF_PROBEMTU; + } else { + ap->nextstop = lmin(ap->nextstop, + rt->rt_rmx.rmx_expire); + } + } + + return 0; +} + +#define MTUTIMO_DEFAULT (60*1) + +static void +in6_mtutimo(void *rock) +{ + struct radix_node_head *rnh = rock; + struct mtuex_arg arg; + struct timeval atv; + int s; + + arg.rnh = rnh; + arg.nextstop = time_second + MTUTIMO_DEFAULT; + s = splnet(); + rnh->rnh_walktree(rnh, in6_mtuexpire, &arg); + splx(s); + + atv.tv_usec = 0; + atv.tv_sec = arg.nextstop; + if (atv.tv_sec < time_second) { + printf("invalid mtu expiration time on routing table\n"); + arg.nextstop = time_second + 30; /*last resort*/ + } + timeout(in6_mtutimo, rock, tvtohz(&atv)); +} + +/* + * Initialize our routing tree. + */ +int +in6_inithead(void **head, int off) +{ + struct radix_node_head *rnh; + + if (!rn_inithead(head, off)) + return 0; + + if (head != (void **)&rt_tables[AF_INET6]) /* BOGUS! */ + return 1; /* only do this for the real routing table */ + + rnh = *head; + rnh->rnh_addaddr = in6_addroute; + rnh->rnh_matchaddr = in6_matroute; + rnh->rnh_close = in6_clsroute; + in6_rtqtimo(rnh); /* kick off timeout first time */ + in6_mtutimo(rnh); /* kick off timeout first time */ + return 1; +} diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index a1aa13e..4117c20 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -13,7 +13,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -98,10 +98,6 @@ struct in6_ifaddr { struct sockaddr_in6 ia_prefixmask; /* prefix mask */ u_int32_t ia_plen; /* prefix length */ struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */ -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) - LIST_HEAD(in6_multihead, in6_multi) ia6_multiaddrs; - /* list of multicast addresses */ -#endif int ia6_flags; struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ @@ -189,7 +185,7 @@ struct icmp6_ifstat { u_int64_t ifs6_in_mlddone; /* - * Output statistics. We should solve unresolved routing problem... + * Output statistics. We should solve unresolved routing problem... */ /* ipv6IfIcmpOutMsgs, total # of output messages */ u_int64_t ifs6_out_msg; @@ -428,34 +424,14 @@ extern struct ifqueue ip6intrq; /* IP6 packet input queue */ extern struct in6_addr zeroin6_addr; extern u_char inet6ctlerrmap[]; extern u_long in6_maxmtu; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPMADDR); #endif /* MALLOC_DECLARE */ -#endif /* * Macro for finding the internet address structure (in6_ifaddr) corresponding * to a given interface (ifnet structure). */ -#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) - -#define IFP_TO_IA6(ifp, ia) \ -/* struct ifnet *ifp; */ \ -/* struct in6_ifaddr *ia; */ \ -do { \ - struct ifaddr *ifa; \ - for (ifa = (ifp)->if_addrlist; ifa; ifa = ifa->ifa_next) { \ - if (!ifa->ifa_addr) \ - continue; \ - if (ifa->ifa_addr->sa_family == AF_INET6) \ - break; \ - } \ - (ia) = (struct in6_ifaddr *)ifa; \ -} while (0) - -#else - #define IFP_TO_IA6(ifp, ia) \ /* struct ifnet *ifp; */ \ /* struct in6_ifaddr *ia; */ \ @@ -471,8 +447,6 @@ do { \ } while (0) #endif /* _KERNEL */ -#endif - /* * Multi-cast membership entry. One for each group/ifp that a PCB * belongs to. @@ -486,20 +460,14 @@ struct in6_multi { LIST_ENTRY(in6_multi) in6m_entry; /* list glue */ struct in6_addr in6m_addr; /* IP6 multicast address */ struct ifnet *in6m_ifp; /* back pointer to ifnet */ -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) - struct in6_ifaddr *in6m_ia; /* back pointer to in6_ifaddr */ -#else struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */ -#endif u_int in6m_refcount; /* # membership claims by sockets */ u_int in6m_state; /* state of the membership */ u_int in6m_timer; /* MLD6 listener report timer */ }; #ifdef _KERNEL -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead; -#endif /* * Structure used by macros below to remember position when stepping through @@ -516,8 +484,6 @@ struct in6_multistep { * returns NLL. */ -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 - #define IN6_LOOKUP_MULTI(addr, ifp, in6m) \ /* struct in6_addr addr; */ \ /* struct ifnet *ifp; */ \ @@ -557,61 +523,6 @@ do { \ IN6_NEXT_MULTI((step), (in6m)); \ } while(0) -#else /* not FreeBSD3 */ - -#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \ -/* struct in6_addr addr; */ \ -/* struct ifnet *ifp; */ \ -/* struct in6_multi *in6m; */ \ -do { \ - register struct in6_ifaddr *ia; \ - \ - IFP_TO_IA6((ifp), ia); \ - if (ia == NULL) \ - (in6m) = NULL; \ - else \ - for ((in6m) = ia->ia6_multiaddrs.lh_first; \ - (in6m) != NULL && \ - !IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr, &(addr)); \ - (in6m) = in6m->in6m_entry.le_next) \ - continue; \ -} while (0) - -/* - * Macro to step through all of the in6_multi records, one at a time. - * The current position is remembered in "step", which the caller must - * provide. IN6_FIRST_MULTI(), below, must be called to initialize "step" - * and get the first record. Both macros return a NULL "in6m" when there - * are no remaining records. - */ -#define IN6_NEXT_MULTI(step, in6m) \ -/* struct in6_multistep step; */ \ -/* struct in6_multi *in6m; */ \ -do { \ - if (((in6m) = (step).i_in6m) != NULL) \ - (step).i_in6m = (in6m)->in6m_entry.le_next; \ - else \ - while ((step).i_ia != NULL) { \ - (in6m) = (step).i_ia->ia6_multiaddrs.lh_first; \ - (step).i_ia = (step).i_ia->ia_next; \ - if ((in6m) != NULL) { \ - (step).i_in6m = (in6m)->in6m_entry.le_next; \ - break; \ - } \ - } \ -} while (0) - -#define IN6_FIRST_MULTI(step, in6m) \ -/* struct in6_multistep step; */ \ -/* struct in6_multi *in6m */ \ -do { \ - (step).i_ia = in6_ifaddr; \ - (step).i_in6m = NULL; \ - IN6_NEXT_MULTI((step), (in6m)); \ -} while (0) - -#endif /* not FreeBSD3 */ - int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *, struct sockaddr_in6 *, int)); struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, @@ -621,12 +532,8 @@ void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *)); extern int in6_ifindex2scopeid __P((int)); extern int in6_mask2len __P((struct in6_addr *)); extern void in6_len2mask __P((struct in6_addr *, int)); -#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) int in6_control __P((struct socket *, - u_long, caddr_t, struct ifnet *, struct proc *)); -#else -int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *)); -#endif + u_long, caddr_t, struct ifnet *, struct proc *)); void in6_savemkludge __P((struct in6_ifaddr *)); void in6_setmaxmtu __P((void)); void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); diff --git a/sys/netinet6/ip6.h b/sys/netinet6/ip6.h new file mode 100644 index 0000000..1dacb10 --- /dev/null +++ b/sys/netinet6/ip6.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET6_IPV6_H_ +#define _NETINET6_IPV6_H_ + +/* + * Definition for internet protocol version 6. + * RFC 2460 + */ + +struct ip6_hdr { + union { + struct ip6_hdrctl { + u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */ + u_int16_t ip6_un1_plen; /* payload length */ + u_int8_t ip6_un1_nxt; /* next header */ + u_int8_t ip6_un1_hlim; /* hop limit */ + } ip6_un1; + u_int8_t ip6_un2_vfc; /* 4 bits version, 4 bits class */ + } ip6_ctlun; + struct in6_addr ip6_src; /* source address */ + struct in6_addr ip6_dst; /* destination address */ +}; + +#define ip6_vfc ip6_ctlun.ip6_un2_vfc +#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow +#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim +#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim + +#define IPV6_VERSION 0x60 +#define IPV6_VERSION_MASK 0xf0 + +#if BYTE_ORDER == BIG_ENDIAN +#define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */ +#define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */ +#endif /* BIG_ENDIAN */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */ +#define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ +#endif /* LITTLE_ENDIAN */ +/* ECN bits proposed by Sally Floyd */ +#define IP6TOS_CE 0x01 /* congestion experienced */ +#define IP6TOS_ECT 0x02 /* ECN-capable transport */ + +/* + * Extension Headers + */ + +struct ip6_ext { + u_char ip6e_nxt; + u_char ip6e_len; +}; + +/* Hop-by-Hop options header */ +/* XXX should we pad it to force alignment on an 8-byte boundary? */ +struct ip6_hbh { + u_int8_t ip6h_nxt; /* next header */ + u_int8_t ip6h_len; /* length in units of 8 octets */ + /* followed by options */ +}; + +/* Destination options header */ +/* XXX should we pad it to force alignment on an 8-byte boundary? */ +struct ip6_dest { + u_int8_t ip6d_nxt; /* next header */ + u_int8_t ip6d_len; /* length in units of 8 octets */ + /* followed by options */ +}; + +/* Option types and related macros */ +#define IP6OPT_PAD1 0x00 /* 00 0 00000 */ +#define IP6OPT_PADN 0x01 /* 00 0 00001 */ +#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ +#define IP6OPT_JUMBO_LEN 6 +#define IP6OPT_RTALERT 0x05 /* 00 0 00101 */ +#define IP6OPT_RTALERT_LEN 4 +#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ +#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ +#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ +#define IP6OPT_MINLEN 2 + +#define IP6OPT_TYPE(o) ((o) & 0xC0) +#define IP6OPT_TYPE_SKIP 0x00 +#define IP6OPT_TYPE_DISCARD 0x40 +#define IP6OPT_TYPE_FORCEICMP 0x80 +#define IP6OPT_TYPE_ICMP 0xC0 + +#define IP6OPT_MUTABLE 0x20 + +/* Routing header */ +struct ip6_rthdr { + u_int8_t ip6r_nxt; /* next header */ + u_int8_t ip6r_len; /* length in units of 8 octets */ + u_int8_t ip6r_type; /* routing type */ + u_int8_t ip6r_segleft; /* segments left */ + /* followed by routing type specific data */ +}; + +/* Type 0 Routing header */ +struct ip6_rthdr0 { + u_int8_t ip6r0_nxt; /* next header */ + u_int8_t ip6r0_len; /* length in units of 8 octets */ + u_int8_t ip6r0_type; /* always zero */ + u_int8_t ip6r0_segleft; /* segments left */ + u_int8_t ip6r0_reserved; /* reserved field */ + u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */ + struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */ +}; + +/* Fragment header */ +struct ip6_frag { + u_int8_t ip6f_nxt; /* next header */ + u_int8_t ip6f_reserved; /* reserved field */ + u_int16_t ip6f_offlg; /* offset, reserved, and flag */ + u_int32_t ip6f_ident; /* identification */ +}; + +#if BYTE_ORDER == BIG_ENDIAN +#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */ +#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */ +#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */ +#else /* BYTE_ORDER == LITTLE_ENDIAN */ +#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */ +#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */ +#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */ +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +/* + * Internet implementation parameters. + */ +#define IPV6_MAXHLIM 255 /* maximun hoplimit */ +#define IPV6_DEFHLIM 64 /* default hlim */ +#define IPV6_FRAGTTL 120 /* ttl for fragment packets, in slowtimo tick */ +#define IPV6_HLIMDEC 1 /* subtracted when forwaeding */ + +#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */ +#define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/ + +/* + * IP6_EXTHDR_CHECK ensures that region between the IP6 header and the + * target header (including IPv6 itself, extension headers and + * TCP/UDP/ICMP6 headers) are continuous. KAME requires drivers + * to store incoming data into one internal mbuf or one or more external + * mbufs(never into two or more internal mbufs). Thus, the third case is + * supposed to never be matched but is prepared just in case. + */ + +#define IP6_EXTHDR_CHECK(m, off, hlen, ret) \ +do { \ + if ((m)->m_next != NULL) { \ + if (((m)->m_flags & M_LOOP) && \ + ((m)->m_len < (off) + (hlen)) && \ + (((m) = m_pullup((m), (off) + (hlen))) == NULL)) { \ + ip6stat.ip6s_exthdrtoolong++; \ + return ret; \ + } else if ((m)->m_flags & M_EXT) { \ + if ((m)->m_len < (off) + (hlen)) { \ + ip6stat.ip6s_exthdrtoolong++; \ + m_freem(m); \ + return ret; \ + } \ + } else { \ + if ((m)->m_len < (off) + (hlen)) { \ + ip6stat.ip6s_exthdrtoolong++; \ + m_freem(m); \ + return ret; \ + } \ + } \ + } else { \ + if ((m)->m_len < (off) + (hlen)) { \ + ip6stat.ip6s_tooshort++; \ + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); \ + m_freem(m); \ + return ret; \ + } \ + } \ +} while (0) + +#endif /* not _NETINET_IPV6_H_ */ diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c new file mode 100644 index 0000000..0a4745b --- /dev/null +++ b/sys/netinet6/ip6_forward.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_ipsec.h" +#include "opt_key.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef IPSEC_IPV6FWD +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#include +#ifdef KEY_DEBUG +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#else +#define DPRINTF(lev,arg) +#define DDO(lev, stmt) +#define DP(x, y, z) +#endif /* KEY_DEBUG */ +#endif /* IPSEC_IPV6FWD */ + +#ifdef IPV6FIREWALL +#include +#endif + +#include + +struct route_in6 ip6_forward_rt; + +/* + * Forward a packet. If some error occurs return the sender + * an icmp packet. Note we can't always generate a meaningful + * icmp message because icmp doesn't have a large enough repertoire + * of codes and types. + * + * If not forwarding, just drop the packet. This could be confusing + * if ipforwarding was zero but some routing protocol was advancing + * us as a gateway to somewhere. However, we must let the routing + * protocol deal with that. + * + */ + +void +ip6_forward(m, srcrt) + struct mbuf *m; + int srcrt; +{ + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + register struct sockaddr_in6 *dst; + register struct rtentry *rt; + int error, type = 0, code = 0; + struct mbuf *mcopy; +#ifdef IPSEC_IPV6FWD + struct secpolicy *sp = NULL; +#endif + +#ifdef IPSEC_IPV6FWD + /* + * Check AH/ESP integrity. + */ + /* + * Don't increment ip6s_cantforward because this is the check + * before forwarding packet actually. + */ + if (ipsec6_in_reject(m, NULL)) { + ipsec6stat.in_polvio++; + m_freem(m); + return; + } +#endif /*IPSEC_IPV6FWD*/ + + if (m->m_flags & (M_BCAST|M_MCAST) || + in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) { + ip6stat.ip6s_cantforward++; + ip6stat.ip6s_badscope++; + /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ + if (ip6_log_time + ip6_log_interval < time_second) { + char addr[INET6_ADDRSTRLEN]; + ip6_log_time = time_second; + strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr)); + log(LOG_DEBUG, + "cannot forward " + "from %s to %s nxt %d received on %s\n", + addr, ip6_sprintf(&ip6->ip6_dst), + ip6->ip6_nxt, + if_name(m->m_pkthdr.rcvif)); + } + m_freem(m); + return; + } + + if (ip6->ip6_hlim <= IPV6_HLIMDEC) { + /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ + icmp6_error(m, ICMP6_TIME_EXCEEDED, + ICMP6_TIME_EXCEED_TRANSIT, 0); + return; + } + ip6->ip6_hlim -= IPV6_HLIMDEC; + +#ifdef IPSEC_IPV6FWD + /* get a security policy for this packet */ + sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); + if (sp == NULL) { + ipsec6stat.out_inval++; + ip6stat.ip6s_cantforward++; + /* XXX: any icmp ? */ + m_freem(m); + return; + } + + error = 0; + + /* check policy */ + switch (sp->policy) { + case IPSEC_POLICY_DISCARD: + /* + * This packet is just discarded. + */ + ipsec6stat.out_polvio++; + ip6stat.ip6s_cantforward++; + key_freesp(sp); + /* XXX: any icmp ? */ + m_freem(m); + return; + + case IPSEC_POLICY_BYPASS: + case IPSEC_POLICY_NONE: + /* no need to do IPsec. */ + key_freesp(sp); + goto skip_ipsec; + + case IPSEC_POLICY_IPSEC: + if (sp->req == NULL) { + /* XXX should be panic ? */ + printf("ip6_forward: No IPsec request specified.\n"); + ip6stat.ip6s_cantforward++; + key_freesp(sp); + /* XXX: any icmp ? */ + m_freem(m); + return; + } + /* do IPsec */ + break; + + case IPSEC_POLICY_ENTRUST: + default: + /* should be panic ?? */ + printf("ip6_forward: Invalid policy found. %d\n", sp->policy); + key_freesp(sp); + goto skip_ipsec; + } + + { + struct ipsec_output_state state; + + /* + * All the extension headers will become inaccessible + * (since they can be encrypted). + * Don't panic, we need no more updates to extension headers + * on inner IPv6 packet (since they are now encapsulated). + * + * IPv6 [ESP|AH] IPv6 [extension headers] payload + */ + bzero(&state, sizeof(state)); + state.m = m; + state.ro = NULL; /* update at ipsec6_output_tunnel() */ + state.dst = NULL; /* update at ipsec6_output_tunnel() */ + + error = ipsec6_output_tunnel(&state, sp, 0); + + m = state.m; + /* XXX allocate a route (ro, dst) again later */ + key_freesp(sp); + + if (error) { + /* mbuf is already reclaimed in ipsec6_output_tunnel. */ + switch (error) { + case EHOSTUNREACH: + case ENETUNREACH: + case EMSGSIZE: + case ENOBUFS: + case ENOMEM: + break; + default: + printf("ip6_output (ipsec): error code %d\n", error); + /*fall through*/ + case ENOENT: + /* don't show these error codes to the user */ + break; + } + ip6stat.ip6s_cantforward++; + /* XXX: any icmp ? */ + m_freem(m); + return; + } + } + skip_ipsec: +#endif /* IPSEC_IPV6FWD */ + + dst = &ip6_forward_rt.ro_dst; + if (!srcrt) { + /* + * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst + */ + if (ip6_forward_rt.ro_rt == 0 || + (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) { + if (ip6_forward_rt.ro_rt) { + RTFREE(ip6_forward_rt.ro_rt); + ip6_forward_rt.ro_rt = 0; + } + /* this probably fails but give it a try again */ + rtalloc_ign((struct route *)&ip6_forward_rt, + RTF_PRCLONING); + } + + if (ip6_forward_rt.ro_rt == 0) { + ip6stat.ip6s_noroute++; + /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ + icmp6_error(m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + return; + } + } else if ((rt = ip6_forward_rt.ro_rt) == 0 || + !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) { + if (ip6_forward_rt.ro_rt) { + RTFREE(ip6_forward_rt.ro_rt); + ip6_forward_rt.ro_rt = 0; + } + bzero(dst, sizeof(*dst)); + dst->sin6_len = sizeof(struct sockaddr_in6); + dst->sin6_family = AF_INET6; + dst->sin6_addr = ip6->ip6_dst; + + rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); + if (ip6_forward_rt.ro_rt == 0) { + ip6stat.ip6s_noroute++; + /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ + icmp6_error(m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + return; + } + } + rt = ip6_forward_rt.ro_rt; + if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){ + in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); + icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu); + return; + } + + if (rt->rt_flags & RTF_GATEWAY) + dst = (struct sockaddr_in6 *)rt->rt_gateway; + /* + * Save at most 528 bytes of the packet in case + * we need to generate an ICMP6 message to the src. + * Thanks to M_EXT, in most cases copy will not occur. + */ + mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); + + /* + * If we are to forward the packet using the same interface + * as one we got the packet from, perhaps we should send a redirect + * to sender to shortcut a hop. + * Only send redirect if source is sending directly to us, + * and if packet was not source routed (or has any options). + * Also, don't send redirect if forwarding using a route + * modified by a redirect. + */ + if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && + (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) + type = ND_REDIRECT; + +#ifdef IPV6FIREWALL + /* + * Check with the firewall... + */ + if (ip6_fw_chk_ptr) { + u_short port = 0; + /* If ipfw says divert, we have to just drop packet */ + if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) { + m_freem(m); + goto freecopy; + } + if (!m) + goto freecopy; + } +#endif + + error = nd6_output(rt->rt_ifp, m, dst, rt); + if (error) { + in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); + ip6stat.ip6s_cantforward++; + } else { + ip6stat.ip6s_forward++; + in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); + if (type) + ip6stat.ip6s_redirectsent++; + else { + if (mcopy) + goto freecopy; + } + } + if (mcopy == NULL) + return; + + switch (error) { + case 0: + if (type == ND_REDIRECT) { + icmp6_redirect_output(mcopy, rt); + return; + } + goto freecopy; + + case EMSGSIZE: + /* xxx MTU is constant in PPP? */ + goto freecopy; + + case ENOBUFS: + /* Tell source to slow down like source quench in IP? */ + goto freecopy; + + case ENETUNREACH: /* shouldn't happen, checked above */ + case EHOSTUNREACH: + case ENETDOWN: + case EHOSTDOWN: + default: + type = ICMP6_DST_UNREACH; + code = ICMP6_DST_UNREACH_ADDR; + break; + } + icmp6_error(mcopy, type, code, 0); + return; + + freecopy: + m_freem(mcopy); + return; +} diff --git a/sys/netinet6/ip6_fw.h b/sys/netinet6/ip6_fw.h new file mode 100644 index 0000000..a356ac3 --- /dev/null +++ b/sys/netinet6/ip6_fw.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1993 Daniel Boulet + * Copyright (c) 1994 Ugen J.S.Antsilevich + * + * Redistribution and use in source forms, with and without modification, + * are permitted provided that this entire comment appears intact. + * + * Redistribution in binary form may occur without any restrictions. + * Obviously, it would be nice if you gave credit where credit is due + * but requiring it would be too onerous. + * + * This software is provided ``AS IS'' without any warranties of any kind. + * + * $Id: ip6_fw.h,v 1.1 1999/08/06 14:10:09 itojun Exp $ + * $FreeBSD$ + */ + +#ifndef _IP6_FW_H +#define _IP6_FW_H + +#include + +/* + * This union structure identifies an interface, either explicitly + * by name or implicitly by IP address. The flags IP_FW_F_IIFNAME + * and IP_FW_F_OIFNAME say how to interpret this structure. An + * interface unit number of -1 matches any unit number, while an + * IP address of 0.0.0.0 indicates matches any interface. + * + * The receive and transmit interfaces are only compared against the + * the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE) + * is set. Note some packets lack a receive or transmit interface + * (in which case the missing "interface" never matches). + */ + +union ip6_fw_if { + struct in6_addr fu_via_ip6; /* Specified by IPv6 address */ + struct { /* Specified by interface name */ +#define FW_IFNLEN IFNAMSIZ + char name[FW_IFNLEN]; + short unit; /* -1 means match any unit */ + } fu_via_if; +}; + +/* + * Format of an IP firewall descriptor + * + * fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order. + * fw_flg and fw_n*p are stored in host byte order (of course). + * Port numbers are stored in HOST byte order. + * Warning: setsockopt() will fail if sizeof(struct ip_fw) > MLEN (108) + */ + +struct ip6_fw { + u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ + struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */ + /* Mask for src and dest IPv6 addr */ + struct in6_addr fw_smsk, fw_dmsk; + u_short fw_number; /* Rule number */ + u_short fw_flg; /* Flags word */ +#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */ + /* Array of port numbers to match */ + u_short fw_pts[IPV6_FW_MAX_PORTS]; + u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */ + u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ +#define IPV6_FW_ICMPTYPES_DIM (32 / (sizeof(unsigned) * 8)) + /* ICMP types bitmap */ + unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM]; + long timestamp; /* timestamp (tv_sec) of last match */ + /* Incoming and outgoing interfaces */ + union ip6_fw_if fw_in_if, fw_out_if; + union { + u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */ + u_short fu_skipto_rule; /* SKIPTO command rule number */ + u_short fu_reject_code; /* REJECT response code */ + } fw_un; + u_char fw_prot; /* IPv6 protocol */ + u_char fw_nports; /* N'of src ports and # of dst ports */ + /* in ports array (dst ports follow */ + /* src ports; max of 10 ports in all; */ + /* count of 0 means match all ports) */ +}; + +#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) +#define IPV6_FW_SETNSRCP(rule, n) do { \ + (rule)->fw_nports &= ~0x0f; \ + (rule)->fw_nports |= (n); \ + } while (0) +#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) +#define IPV6_FW_SETNDSTP(rule, n) do { \ + (rule)->fw_nports &= ~0xf0; \ + (rule)->fw_nports |= (n) << 4;\ + } while (0) + +#define fw_divert_port fw_un.fu_divert_port +#define fw_skipto_rule fw_un.fu_skipto_rule +#define fw_reject_code fw_un.fu_reject_code + +struct ip6_fw_chain { + LIST_ENTRY(ip6_fw_chain) chain; + struct ip6_fw *rule; +}; + +/* + * Values for "flags" field . + */ +#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */ +#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */ +#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ +#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ + +#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ +#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */ +#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */ +#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */ +#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */ +#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */ +#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */ +#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ + +#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */ + +#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min * + * and max range (stored in host byte * + * order). */ + +#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min * + * and max range (stored in host byte * + * order). */ + +/* In interface by name/unit (not IP) */ +#define IPV6_FW_F_IIFNAME 0x0400 +/* Out interface by name/unit (not IP) */ +#define IPV6_FW_F_OIFNAME 0x0800 + +#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */ +#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */ + +#define IPV6_FW_F_FRAG 0x4000 /* Fragment */ + +#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ + +#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */ + +/* + * For backwards compatibility with rules specifying "via iface" but + * not restricted to only "in" or "out" packets, we define this combination + * of bits to represent this configuration. + */ + +#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|\ + IPV6_FW_F_OIFACE) + +/* + * Definitions for REJECT response codes. + * Values less than 256 correspond to ICMP unreachable codes. + */ +#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ + +/* + * Definitions for IPv6 option names. + */ +#define IPV6_FW_IP6OPT_HOPOPT 0x01 +#define IPV6_FW_IP6OPT_ROUTE 0x02 +#define IPV6_FW_IP6OPT_FRAG 0x04 +#define IPV6_FW_IP6OPT_ESP 0x08 +#define IPV6_FW_IP6OPT_AH 0x10 +#define IPV6_FW_IP6OPT_NONXT 0x20 +#define IPV6_FW_IP6OPT_OPTS 0x40 + +/* + * Definitions for TCP flags. + */ +#define IPV6_FW_TCPF_FIN TH_FIN +#define IPV6_FW_TCPF_SYN TH_SYN +#define IPV6_FW_TCPF_RST TH_RST +#define IPV6_FW_TCPF_PSH TH_PUSH +#define IPV6_FW_TCPF_ACK TH_ACK +#define IPV6_FW_TCPF_URG TH_URG +#define IPV6_FW_TCPF_ESTAB 0x40 + +/* + * Main firewall chains definitions and global var's definitions. + */ +#ifdef _KERNEL + +/* + * Function definitions. + */ +void ip6_fw_init(void); + +/* Firewall hooks */ +struct ip6_hdr; +typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*, + u_short *, struct mbuf**)); +typedef int ip6_fw_ctl_t __P((int, struct mbuf**)); +extern ip6_fw_chk_t *ip6_fw_chk_ptr; +extern ip6_fw_ctl_t *ip6_fw_ctl_ptr; + +#endif /* _KERNEL */ + +#endif /* _IP6_FW_H */ diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c new file mode 100644 index 0000000..dc12b0f --- /dev/null +++ b/sys/netinet6/ip6_input.c @@ -0,0 +1,1016 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 + */ + +#include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_ipsec.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#ifdef INET +#include +#include +#endif /*INET*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef IPV6FIREWALL +#include +#endif + +#ifdef ALTQ +#include +#endif + +#include + +/* we need it for NLOOP. */ +#include "loop.h" +/* #include "faith.h" */ + +/* #include "gif.h" */ + +#include + +extern struct domain inet6domain; +extern struct ip6protosw inet6sw[]; + +u_char ip6_protox[IPPROTO_MAX]; +static int ip6qmaxlen = IFQ_MAXLEN; +struct in6_ifaddr *in6_ifaddr; +struct ifqueue ip6intrq; + +int ip6_forward_srcrt; /* XXX */ +int ip6_sourcecheck; /* XXX */ +int ip6_sourcecheck_interval; /* XXX */ + +#ifdef IPV6FIREWALL +/* firewall hooks */ +ip6_fw_chk_t *ip6_fw_chk_ptr; +ip6_fw_ctl_t *ip6_fw_ctl_ptr; +#endif + +struct ip6stat ip6stat; + +static void ip6_init2 __P((void *)); + +static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); + +#if defined(PTR) +extern int ip6_protocol_tr; + +int ptr_in6 __P((struct mbuf *, struct mbuf **)); +extern void ip_forward __P((struct mbuf *, int)); +#endif + +/* + * IP6 initialization: fill in IP6 protocol switch table. + * All protocols not implemented in kernel go to raw IP6 protocol handler. + */ +void +ip6_init() +{ + register struct ip6protosw *pr; + register int i; + struct timeval tv; + + pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); + if (pr == 0) + panic("ip6_init"); + for (i = 0; i < IPPROTO_MAX; i++) + ip6_protox[i] = pr - inet6sw; + for (pr = (struct ip6protosw *)inet6domain.dom_protosw; + pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW; pr++) + if (pr->pr_domain->dom_family == PF_INET6 && + pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) + ip6_protox[pr->pr_protocol] = pr - inet6sw; + ip6intrq.ifq_maxlen = ip6qmaxlen; + nd6_init(); + frag6_init(); +#ifdef IPV6FIREWALL + ip6_fw_init(); +#endif + /* + * in many cases, random() here does NOT return random number + * as initialization during bootstrap time occur in fixed order. + */ + microtime(&tv); + ip6_flow_seq = random() ^ tv.tv_usec; +} + +static void +ip6_init2(dummy) + void *dummy; +{ + int i; + int ret; + + /* get EUI64 from somewhere */ + ret = in6_ifattach_getifid(NULL); + + /* + * to route local address of p2p link to loopback, + * assign loopback address first. + */ + for (i = 0; i < NLOOP; i++) + in6_ifattach(&loif[i], IN6_IFT_LOOP, NULL, 0); + + /* attach pseudo interfaces */ + if (ret == 0) + in6_ifattach_p2p(); + + /* nd6_timer_init */ + timeout(nd6_timer, (caddr_t)0, hz); + /* router renumbering prefix list maintenance */ + timeout(in6_rr_timer, (caddr_t)0, hz); +} + +/* cheat */ +SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, ip6_init2, NULL); + +/* + * IP6 input interrupt handling. Just pass the packet to ip6_input. + */ +void +ip6intr() +{ + int s; + struct mbuf *m; + + for (;;) { + s = splimp(); + IF_DEQUEUE(&ip6intrq, m); + splx(s); + if (m == 0) + return; + ip6_input(m); + } +} + +NETISR_SET(NETISR_IPV6, ip6intr); + +extern struct route_in6 ip6_forward_rt; + +void +ip6_input(m) + struct mbuf *m; +{ + struct ip6_hdr *ip6; + int off = sizeof(struct ip6_hdr), nest; + u_int32_t plen; + u_int32_t rtalert = ~0; + int nxt, ours = 0; + struct ifnet *deliverifp = NULL; + +#ifdef IPSEC + /* + * should the inner packet be considered authentic? + * see comment in ah4_input(). + */ + if (m) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } +#endif + + /* + * mbuf statistics by kazu + */ + if (m->m_flags & M_EXT) { + if (m->m_next) + ip6stat.ip6s_mext2m++; + else + ip6stat.ip6s_mext1++; + } else { + if (m->m_next) { + if (m->m_flags & M_LOOP) { + ip6stat.ip6s_m2m[loif[0].if_index]++; /*XXX*/ + } else if (m->m_pkthdr.rcvif->if_index <= 31) + ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; + else + ip6stat.ip6s_m2m[0]++; + } else + ip6stat.ip6s_m1++; + } + + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); + ip6stat.ip6s_total++; + + IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /*nothing*/); + + if (m->m_len < sizeof(struct ip6_hdr)) { + struct ifnet *inifp; + inifp = m->m_pkthdr.rcvif; + if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) { + ip6stat.ip6s_toosmall++; + in6_ifstat_inc(inifp, ifs6_in_hdrerr); + return; + } + } + + ip6 = mtod(m, struct ip6_hdr *); + + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + ip6stat.ip6s_badvers++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); + goto bad; + } + + ip6stat.ip6s_nxthist[ip6->ip6_nxt]++; + +#ifdef IPV6FIREWALL + /* + * Check with the firewall... + */ + if (ip6_fw_chk_ptr) { + u_short port = 0; + /* If ipfw says divert, we have to just drop packet */ + /* use port as a dummy argument */ + if ((*ip6_fw_chk_ptr)(&ip6, NULL, &port, &m)) { + m_freem(m); + m = NULL; + } + if (!m) + return; + } +#endif + +#ifdef ALTQ + if (altq_input != NULL && (*altq_input)(m, AF_INET6) == 0) { + /* packet is dropped by traffic conditioner */ + return; + } +#endif + + /* + * Scope check + */ + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || + IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; + } + if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || + IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { + if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } else { + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; + } + } + + if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } + } else { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] + = htons(m->m_pkthdr.rcvif->if_index); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] + = htons(m->m_pkthdr.rcvif->if_index); + } + +#if defined(PTR) + /* + * + */ + if (ip6_protocol_tr) + { + struct mbuf *m1 = NULL; + + switch (ptr_in6(m, &m1)) + { + case IPPROTO_IP: goto mcastcheck; + case IPPROTO_IPV4: ip_forward(m1, 0); break; + case IPPROTO_IPV6: ip6_forward(m1, 0); break; + case IPPROTO_MAX: /* discard this packet */ + default: + } + + if (m != m1) + m_freem(m); + + return; + } + + mcastcheck: +#endif + + /* + * Multicast check + */ + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + struct in6_multi *in6m = 0; + + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast); + /* + * See if we belong to the destination multicast group on the + * arrival interface. + */ + IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m); + if (in6m) + ours = 1; + else { + ip6stat.ip6s_notmember++; + ip6stat.ip6s_cantforward++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); + goto bad; + } + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } + + /* + * Unicast check + */ + if (ip6_forward_rt.ro_rt == 0 || + !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + &ip6_forward_rt.ro_dst.sin6_addr)) { + if (ip6_forward_rt.ro_rt) { + RTFREE(ip6_forward_rt.ro_rt); + ip6_forward_rt.ro_rt = 0; + } + bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); + ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6); + ip6_forward_rt.ro_dst.sin6_family = AF_INET6; + ip6_forward_rt.ro_dst.sin6_addr = ip6->ip6_dst; + + rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); + } + +#define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key)) + + /* + * Accept the packet if the forwarding interface to the destination + * according to the routing table is the loopback interface, + * unless the associated route has a gateway. + * Note that this approach causes to accept a packet if there is a + * route to the loopback interface for the destination of the packet. + * But we think it's even useful in some situations, e.g. when using + * a special daemon which wants to intercept the packet. + */ + if (ip6_forward_rt.ro_rt && + (ip6_forward_rt.ro_rt->rt_flags & + (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && + /* + * The comparison of the destination and the key of the rtentry + * has already done through looking up the routing table, + * so no need to do such a comparison here again. + */ + ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { + struct in6_ifaddr *ia6 = + (struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa; + /* packet to tentative address must not be received */ + if (ia6->ia6_flags & IN6_IFF_ANYCAST) + m->m_flags |= M_ANYCAST6; + if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) { + /* this interface is ready */ + ours = 1; + deliverifp = ia6->ia_ifp; /* correct? */ + goto hbhcheck; + } else { + /* this interface is not ready, fall through */ + } + } + + /* + * FAITH(Firewall Aided Internet Translator) + */ +#if defined(NFAITH) && 0 < NFAITH + if (ip6_keepfaith) { + if (ip6_forward_rt.ro_rt && ip6_forward_rt.ro_rt->rt_ifp + && ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) { + /* XXX do we need more sanity checks? */ + ours = 1; + deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /*faith*/ + goto hbhcheck; + } + } +#endif + + /* + * Now there is no reason to process the packet if it's not our own + * and we're not a router. + */ + if (!ip6_forwarding) { + ip6stat.ip6s_cantforward++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); + goto bad; + } + + hbhcheck: + /* + * Process Hop-by-Hop options header if it's contained. + * m may be modified in ip6_hopopts_input(). + * If a JumboPayload option is included, plen will also be modified. + */ + plen = (u_int32_t)ntohs(ip6->ip6_plen); + if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { + if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) { + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); + return; /* m have already been freed */ + } + /* adjust pointer */ + ip6 = mtod(m, struct ip6_hdr *); + nxt = ((struct ip6_hbh *)(ip6 + 1))->ip6h_nxt; + + /* + * accept the packet if a router alert option is included + * and we act as an IPv6 router. + */ + if (rtalert != ~0 && ip6_forwarding) + ours = 1; + } else + nxt = ip6->ip6_nxt; + + /* + * Check that the amount of data in the buffers + * is as at least much as the IPv6 header would have us expect. + * Trim mbufs if longer than we expect. + * Drop packet if shorter than we expect. + */ + if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) { + ip6stat.ip6s_tooshort++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); + goto bad; + } + if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) { + if (m->m_len == m->m_pkthdr.len) { + m->m_len = sizeof(struct ip6_hdr) + plen; + m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; + } else + m_adj(m, sizeof(struct ip6_hdr) + plen - m->m_pkthdr.len); + } + + /* + * Forward if desirable. + */ + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + if (!ours) { + m_freem(m); + return; + } + } else if (!ours) { + ip6_forward(m, 0); + return; + } + + /* + * Tell launch routine the next header + */ +#if defined(__NetBSD__) && defined(IFA_STATS) + if (IFA_STATS && deliverifp != NULL) { + struct in6_ifaddr *ia6; + ip6 = mtod(m, struct ip6_hdr *); + ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); + if (ia6) + ia6->ia_ifa.ifa_data.ifad_inbytes += m->m_pkthdr.len; + } +#endif + ip6stat.ip6s_delivered++; + in6_ifstat_inc(deliverifp, ifs6_in_deliver); + nest = 0; + while (nxt != IPPROTO_DONE) { + if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { + ip6stat.ip6s_toomanyhdr++; + goto bad; + } + + /* + * protection against faulty packet - there should be + * more sanity checks in header chain processing. + */ + if (m->m_pkthdr.len < off) { + ip6stat.ip6s_tooshort++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); + goto bad; + } + + nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); + } + return; + bad: + m_freem(m); +} + +/* + * Hop-by-Hop options header processing. If a valid jumbo payload option is + * included, the real payload length will be stored in plenp. + */ +static int +ip6_hopopts_input(plenp, rtalertp, mp, offp) + u_int32_t *plenp; + u_int32_t *rtalertp; /* XXX: should be stored more smart way */ + struct mbuf **mp; + int *offp; +{ + register struct mbuf *m = *mp; + int off = *offp, hbhlen; + struct ip6_hbh *hbh; + u_int8_t *opt; + + /* validation of the length of the header */ + IP6_EXTHDR_CHECK(m, off, sizeof(*hbh), -1); + hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); + hbhlen = (hbh->ip6h_len + 1) << 3; + + IP6_EXTHDR_CHECK(m, off, hbhlen, -1); + hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); + off += hbhlen; + hbhlen -= sizeof(struct ip6_hbh); + opt = (u_int8_t *)hbh + sizeof(struct ip6_hbh); + + if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh), + hbhlen, rtalertp, plenp) < 0) + return(-1); + + *offp = off; + *mp = m; + return(0); +} + +/* + * Search header for all Hop-by-hop options and process each option. + * This function is separate from ip6_hopopts_input() in order to + * handle a case where the sending node itself process its hop-by-hop + * options header. In such a case, the function is called from ip6_output(). + */ +int +ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) + struct mbuf *m; + u_int8_t *opthead; + int hbhlen; + u_int32_t *rtalertp; + u_int32_t *plenp; +{ + struct ip6_hdr *ip6; + int optlen = 0; + u_int8_t *opt = opthead; + u_int16_t rtalert_val; + + for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { + switch(*opt) { + case IP6OPT_PAD1: + optlen = 1; + break; + case IP6OPT_PADN: + if (hbhlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + optlen = *(opt + 1) + 2; + break; + case IP6OPT_RTALERT: + /* XXX may need check for alignment */ + if (hbhlen < IP6OPT_RTALERT_LEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) + /* XXX: should we discard the packet? */ + log(LOG_ERR, "length of router alert opt is inconsitent(%d)", + *(opt + 1)); + optlen = IP6OPT_RTALERT_LEN; + bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); + *rtalertp = ntohs(rtalert_val); + break; + case IP6OPT_JUMBO: + /* XXX may need check for alignment */ + if (hbhlen < IP6OPT_JUMBO_LEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) + /* XXX: should we discard the packet? */ + log(LOG_ERR, "length of jumbopayload opt " + "is inconsistent(%d)", + *(opt + 1)); + optlen = IP6OPT_JUMBO_LEN; + + bcopy(opt + 2, plenp, sizeof(*plenp)); + *plenp = htonl(*plenp); + if (*plenp <= IPV6_MAXPACKET) { + /* + * jumbo payload length must be larger + * than 65535 + */ + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt + 2 - opthead); + return(-1); + } + + ip6 = mtod(m, struct ip6_hdr *); + if (ip6->ip6_plen) { + /* + * IPv6 packets that have non 0 payload length + * must not contain a jumbo paylod option. + */ + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt - opthead); + return(-1); + } + break; + default: /* unknown option */ + if (hbhlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if ((optlen = ip6_unknown_opt(opt, m, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt - opthead)) == -1) + return(-1); + optlen += 2; + break; + } + } + + return(0); + + bad: + m_freem(m); + return(-1); +} + +/* + * Unknown option processing. + * The third argument `off' is the offset from the IPv6 header to the option, + * which is necessary if the IPv6 header the and option header and IPv6 header + * is not continuous in order to return an ICMPv6 error. + */ +int +ip6_unknown_opt(optp, m, off) + u_int8_t *optp; + struct mbuf *m; + int off; +{ + struct ip6_hdr *ip6; + + switch(IP6OPT_TYPE(*optp)) { + case IP6OPT_TYPE_SKIP: /* ignore the option */ + return((int)*(optp + 1)); + case IP6OPT_TYPE_DISCARD: /* silently discard */ + m_freem(m); + return(-1); + case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); + return(-1); + case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ + ip6stat.ip6s_badoptions++; + ip6 = mtod(m, struct ip6_hdr *); + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + (m->m_flags & (M_BCAST|M_MCAST))) + m_freem(m); + else + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_OPTION, off); + return(-1); + } + + m_freem(m); /* XXX: NOTREACHED */ + return(-1); +} + +/* + * Create the "control" list for this pcb + */ +void +ip6_savecontrol(in6p, mp, ip6, m) + register struct inpcb *in6p; + register struct mbuf **mp; + register struct ip6_hdr *ip6; + register struct mbuf *m; +{ + struct proc *p = curproc; /* XXX */ + int privileged; + + privileged = 0; + if (p && !suser(p)) + privileged++; + +#ifdef SO_TIMESTAMP + if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { + struct timeval tv; + + microtime(&tv); + *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), + SCM_TIMESTAMP, SOL_SOCKET); + if (*mp) + mp = &(*mp)->m_next; + } +#endif + if (in6p->in6p_flags & IN6P_RECVDSTADDR) { + *mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst, + sizeof(struct in6_addr), IPV6_RECVDSTADDR, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + } + + /* RFC 2292 sec. 5 */ + if (in6p->in6p_flags & IN6P_PKTINFO) { + struct in6_pktinfo pi6; + bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); + if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr)) + pi6.ipi6_addr.s6_addr16[1] = 0; + pi6.ipi6_ifindex = (m && m->m_pkthdr.rcvif) + ? m->m_pkthdr.rcvif->if_index + : 0; + *mp = sbcreatecontrol((caddr_t) &pi6, + sizeof(struct in6_pktinfo), IPV6_PKTINFO, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + } + if (in6p->in6p_flags & IN6P_HOPLIMIT) { + int hlim = ip6->ip6_hlim & 0xff; + *mp = sbcreatecontrol((caddr_t) &hlim, + sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + } + /* IN6P_NEXTHOP - for outgoing packet only */ + + /* + * IPV6_HOPOPTS socket option. We require super-user privilege + * for the option, but it might be too strict, since there might + * be some hop-by-hop options which can be returned to normal user. + * See RFC 2292 section 6. + */ + if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { + /* + * Check if a hop-by-hop options header is contatined in the + * received packet, and if so, store the options as ancillary + * data. Note that a hop-by-hop options header must be + * just after the IPv6 header, which fact is assured through + * the IPv6 input processing. + */ + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { + struct ip6_hbh *hbh = (struct ip6_hbh *)(ip6 + 1); + + /* + * XXX: We copy whole the header even if a jumbo + * payload option is included, which option is to + * be removed before returning in the RFC 2292. + * But it's too painful operation... + */ + *mp = sbcreatecontrol((caddr_t)hbh, + (hbh->ip6h_len + 1) << 3, + IPV6_HOPOPTS, IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + } + } + + /* IPV6_DSTOPTS and IPV6_RTHDR socket options */ + if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) { + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);; + + /* + * Search for destination options headers or routing + * header(s) through the header chain, and stores each + * header as ancillary data. + * Note that the order of the headers remains in + * the chain of ancillary data. + */ + while(1) { /* is explicit loop prevention necessary? */ + struct ip6_ext *ip6e = + (struct ip6_ext *)(mtod(m, caddr_t) + off); + + switch(nxt) { + case IPPROTO_DSTOPTS: + if (!in6p->in6p_flags & IN6P_DSTOPTS) + break; + + /* + * We also require super-user privilege for + * the option. + * See the comments on IN6_HOPOPTS. + */ + if (!privileged) + break; + + *mp = sbcreatecontrol((caddr_t)ip6e, + (ip6e->ip6e_len + 1) << 3, + IPV6_DSTOPTS, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + break; + + case IPPROTO_ROUTING: + if (!in6p->in6p_flags & IN6P_RTHDR) + break; + + *mp = sbcreatecontrol((caddr_t)ip6e, + (ip6e->ip6e_len + 1) << 3, + IPV6_RTHDR, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + break; + + case IPPROTO_UDP: + case IPPROTO_TCP: + case IPPROTO_ICMPV6: + default: + /* + * stop search if we encounter an upper + * layer protocol headers. + */ + goto loopend; + + case IPPROTO_HOPOPTS: + case IPPROTO_AH: /* is it possible? */ + break; + } + + /* proceed with the next header. */ + if (nxt == IPPROTO_AH) + off += (ip6e->ip6e_len + 2) << 2; + else + off += (ip6e->ip6e_len + 1) << 3; + nxt = ip6e->ip6e_nxt; + } + loopend: + } + if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { + /* to be done */ + } + if ((in6p->in6p_flags & IN6P_DSTOPTS) && privileged) { + /* to be done */ + } + /* IN6P_RTHDR - to be done */ +} + +/* + * Get pointer to the previous header followed by the header + * currently processed. + * XXX: This function supposes that + * M includes all headers, + * the next header field and the header length field of each header + * are valid, and + * the sum of each header length equals to OFF. + * Because of these assumptions, this function must be called very + * carefully. Moreover, it will not be used in the near future when + * we develop `neater' mechanism to process extension headers. + */ +char * +ip6_get_prevhdr(m, off) + struct mbuf *m; + int off; +{ + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + + if (off == sizeof(struct ip6_hdr)) + return(&ip6->ip6_nxt); + else { + int len, nxt; + struct ip6_ext *ip6e = NULL; + + nxt = ip6->ip6_nxt; + len = sizeof(struct ip6_hdr); + while (len < off) { + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); + + switch(nxt) { + case IPPROTO_FRAGMENT: + len += sizeof(struct ip6_frag); + break; + case IPPROTO_AH: + len += (ip6e->ip6e_len + 2) << 2; + break; + default: + len += (ip6e->ip6e_len + 1) << 3; + break; + } + nxt = ip6e->ip6e_nxt; + } + if (ip6e) + return(&ip6e->ip6e_nxt); + else + return NULL; + } +} + +/* + * System control for IP6 + */ + +u_char inet6ctlerrmap[PRC_NCMDS] = { + 0, 0, 0, 0, + 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, + EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, + EMSGSIZE, EHOSTUNREACH, 0, 0, + 0, 0, 0, 0, + ENOPROTOOPT +}; diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c new file mode 100644 index 0000000..c452ad3 --- /dev/null +++ b/sys/netinet6/ip6_output.c @@ -0,0 +1,2176 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 + */ + +#include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_ipsec.h" +#include "opt_key.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef IPSEC +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#include +#ifdef KEY_DEBUG +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#else +#define DPRINTF(lev,arg) +#define DDO(lev, stmt) +#define DP(x, y, z) +#endif /* KEY_DEBUG */ +#endif /* IPSEC */ + +#include "loop.h" + +#include + +#ifdef IPV6FIREWALL +#include +#endif + +static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); + +struct ip6_exthdrs { + struct mbuf *ip6e_ip6; + struct mbuf *ip6e_hbh; + struct mbuf *ip6e_dest1; + struct mbuf *ip6e_rthdr; + struct mbuf *ip6e_dest2; +}; + +static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, + struct socket *, struct sockopt *sopt)); +static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); +static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); +static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); +static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, + struct ip6_frag **)); +static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t)); +static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); + +/* + * IP6 output. The packet in mbuf chain m contains a skeletal IP6 + * header (with pri, len, nxt, hlim, src, dst). + * This function may modify ver and hlim only. + * The mbuf chain containing the packet will be freed. + * The mbuf opt, if present, will not be freed. + */ +int +ip6_output(m0, opt, ro, flags, im6o, ifpp) + struct mbuf *m0; + struct ip6_pktopts *opt; + struct route_in6 *ro; + int flags; + struct ip6_moptions *im6o; + struct ifnet **ifpp; /* XXX: just for statistics */ +{ + struct ip6_hdr *ip6, *mhip6; + struct ifnet *ifp; + struct mbuf *m = m0; + int hlen, tlen, len, off; + struct route_in6 ip6route; + struct sockaddr_in6 *dst; + int error = 0; + struct in6_ifaddr *ia; + u_long mtu; + u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; + struct ip6_exthdrs exthdrs; + struct in6_addr finaldst; + struct route_in6 *ro_pmtu = NULL; + int hdrsplit = 0; + int needipsec = 0; +#ifdef IPSEC + int needipsectun = 0; + struct socket *so; + struct secpolicy *sp = NULL; + + /* for AH processing. stupid to have "socket" variable in IP layer... */ + so = (struct socket *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + ip6 = mtod(m, struct ip6_hdr *); +#endif /* IPSEC */ + +#define MAKE_EXTHDR(hp,mp) \ + { \ + if (hp) { \ + struct ip6_ext *eh = (struct ip6_ext *)(hp); \ + error = ip6_copyexthdr((mp), (caddr_t)(hp), \ + ((eh)->ip6e_len + 1) << 3); \ + if (error) \ + goto freehdrs; \ + } \ + } + + bzero(&exthdrs, sizeof(exthdrs)); + if (opt) { + /* Hop-by-Hop options header */ + MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); + /* Destination options header(1st part) */ + MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); + /* Routing header */ + MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr); + /* Destination options header(2nd part) */ + MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); + } + +#ifdef IPSEC + /* get a security policy for this packet */ + if (so == NULL) + sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); + else + sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); + + if (sp == NULL) { + ipsec6stat.out_inval++; + goto bad; + } + + error = 0; + + /* check policy */ + switch (sp->policy) { + case IPSEC_POLICY_DISCARD: + /* + * This packet is just discarded. + */ + ipsec6stat.out_polvio++; + goto bad; + + case IPSEC_POLICY_BYPASS: + case IPSEC_POLICY_NONE: + /* no need to do IPsec. */ + needipsec = 0; + break; + + case IPSEC_POLICY_IPSEC: + if (sp->req == NULL) { + /* XXX should be panic ? */ + printf("ip6_output: No IPsec request specified.\n"); + error = EINVAL; + goto bad; + } + needipsec = 1; + break; + + case IPSEC_POLICY_ENTRUST: + default: + printf("ip6_output: Invalid policy found. %d\n", sp->policy); + } +#endif /* IPSEC */ + + /* + * Calculate the total length of the extension header chain. + * Keep the length of the unfragmentable part for fragmentation. + */ + optlen = 0; + if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len; + if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len; + if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len; + unfragpartlen = optlen + sizeof(struct ip6_hdr); + /* NOTE: we don't add AH/ESP length here. do that later. */ + if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len; + + /* + * If we need IPsec, or there is at least one extension header, + * separate IP6 header from the payload. + */ + if ((needipsec || optlen) && !hdrsplit) { + if ((error = ip6_splithdr(m, &exthdrs)) != 0) { + m = NULL; + goto freehdrs; + } + m = exthdrs.ip6e_ip6; + hdrsplit++; + } + + /* adjust pointer */ + ip6 = mtod(m, struct ip6_hdr *); + + /* adjust mbuf packet header length */ + m->m_pkthdr.len += optlen; + plen = m->m_pkthdr.len - sizeof(*ip6); + + /* If this is a jumbo payload, insert a jumbo payload option. */ + if (plen > IPV6_MAXPACKET) { + if (!hdrsplit) { + if ((error = ip6_splithdr(m, &exthdrs)) != 0) { + m = NULL; + goto freehdrs; + } + m = exthdrs.ip6e_ip6; + hdrsplit++; + } + /* adjust pointer */ + ip6 = mtod(m, struct ip6_hdr *); + if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0) + goto freehdrs; + ip6->ip6_plen = 0; + } else + ip6->ip6_plen = htons(plen); + + /* + * Concatenate headers and fill in next header fields. + * Here we have, on "m" + * IPv6 payload + * and we insert headers accordingly. Finally, we should be getting: + * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] + * + * during the header composing process, "m" points to IPv6 header. + * "mprev" points to an extension header prior to esp. + */ + { + u_char *nexthdrp = &ip6->ip6_nxt; + struct mbuf *mprev = m; + + /* + * we treat dest2 specially. this makes IPsec processing + * much easier. + * + * result: IPv6 dest2 payload + * m and mprev will point to IPv6 header. + */ + if (exthdrs.ip6e_dest2) { + if (!hdrsplit) + panic("assumption failed: hdr not split"); + exthdrs.ip6e_dest2->m_next = m->m_next; + m->m_next = exthdrs.ip6e_dest2; + *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt; + ip6->ip6_nxt = IPPROTO_DSTOPTS; + } + +#define MAKE_CHAIN(m,mp,p,i)\ + {\ + if (m) {\ + if (!hdrsplit) \ + panic("assumption failed: hdr not split"); \ + *mtod((m), u_char *) = *(p);\ + *(p) = (i);\ + p = mtod((m), u_char *);\ + (m)->m_next = (mp)->m_next;\ + (mp)->m_next = (m);\ + (mp) = (m);\ + }\ + } + /* + * result: IPv6 hbh dest1 rthdr dest2 payload + * m will point to IPv6 header. mprev will point to the + * extension header prior to dest2 (rthdr in the above case). + */ + MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, + nexthdrp, IPPROTO_HOPOPTS); + MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, + nexthdrp, IPPROTO_DSTOPTS); + MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, + nexthdrp, IPPROTO_ROUTING); + +#ifdef IPSEC + if (!needipsec) + goto skip_ipsec2; + + /* + * pointers after IPsec headers are not valid any more. + * other pointers need a great care too. + * (IPsec routines should not mangle mbufs prior to AH/ESP) + */ + exthdrs.ip6e_dest2 = NULL; + + { + struct ip6_rthdr *rh = NULL; + int segleft_org = 0; + struct ipsec_output_state state; + + if (exthdrs.ip6e_rthdr) { + rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *); + segleft_org = rh->ip6r_segleft; + rh->ip6r_segleft = 0; + } + + bzero(&state, sizeof(state)); + state.m = m; + error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags, + &needipsectun); + m = state.m; + if (error) { + /* mbuf is already reclaimed in ipsec6_output_trans. */ + m = NULL; + switch (error) { + case EHOSTUNREACH: + case ENETUNREACH: + case EMSGSIZE: + case ENOBUFS: + case ENOMEM: + break; + default: + printf("ip6_output (ipsec): error code %d\n", error); + /*fall through*/ + case ENOENT: + /* don't show these error codes to the user */ + error = 0; + break; + } + goto bad; + } + if (exthdrs.ip6e_rthdr) { + /* ah6_output doesn't modify mbuf chain */ + rh->ip6r_segleft = segleft_org; + } + } +skip_ipsec2:; +#endif + } + + /* + * If there is a routing header, replace destination address field + * with the first hop of the routing header. + */ + if (exthdrs.ip6e_rthdr) { + struct ip6_rthdr *rh = + (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr, + struct ip6_rthdr *)); + struct ip6_rthdr0 *rh0; + + finaldst = ip6->ip6_dst; + switch(rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rh0 = (struct ip6_rthdr0 *)rh; + ip6->ip6_dst = rh0->ip6r0_addr[0]; + bcopy((caddr_t)&rh0->ip6r0_addr[1], + (caddr_t)&rh0->ip6r0_addr[0], + sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) + ); + rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst; + break; + default: /* is it possible? */ + error = EINVAL; + goto bad; + } + } + + /* Source address validation */ + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && + (flags & IPV6_DADOUTPUT) == 0) { + error = EOPNOTSUPP; + ip6stat.ip6s_badscope++; + goto bad; + } + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { + error = EOPNOTSUPP; + ip6stat.ip6s_badscope++; + goto bad; + } + + ip6stat.ip6s_localout++; + + /* + * Route packet. + */ + if (ro == 0) { + ro = &ip6route; + bzero((caddr_t)ro, sizeof(*ro)); + } + ro_pmtu = ro; + if (opt && opt->ip6po_rthdr) + ro = &opt->ip6po_route; + dst = (struct sockaddr_in6 *)&ro->ro_dst; + /* + * If there is a cached route, + * check that it is to the same destination + * and is still up. If not, free it and try again. + */ + if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || + !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)0; + } + if (ro->ro_rt == 0) { + bzero(dst, sizeof(*dst)); + dst->sin6_family = AF_INET6; + dst->sin6_len = sizeof(struct sockaddr_in6); + dst->sin6_addr = ip6->ip6_dst; + } +#ifdef IPSEC + if (needipsec && needipsectun) { + struct ipsec_output_state state; + + /* + * All the extension headers will become inaccessible + * (since they can be encrypted). + * Don't panic, we need no more updates to extension headers + * on inner IPv6 packet (since they are now encapsulated). + * + * IPv6 [ESP|AH] IPv6 [extension headers] payload + */ + bzero(&exthdrs, sizeof(exthdrs)); + exthdrs.ip6e_ip6 = m; + + bzero(&state, sizeof(state)); + state.m = m; + state.ro = (struct route *)ro; + state.dst = (struct sockaddr *)dst; + + error = ipsec6_output_tunnel(&state, sp, flags); + + m = state.m; + ro = (struct route_in6 *)state.ro; + dst = (struct sockaddr_in6 *)state.dst; + if (error) { + /* mbuf is already reclaimed in ipsec6_output_tunnel. */ + m0 = m = NULL; + m = NULL; + switch (error) { + case EHOSTUNREACH: + case ENETUNREACH: + case EMSGSIZE: + case ENOBUFS: + case ENOMEM: + break; + default: + printf("ip6_output (ipsec): error code %d\n", error); + /*fall through*/ + case ENOENT: + /* don't show these error codes to the user */ + error = 0; + break; + } + goto bad; + } + + exthdrs.ip6e_ip6 = m; + } +#endif /*IPESC*/ + + if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + /* Unicast */ + +#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) +#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) + /* xxx + * interface selection comes here + * if an interface is specified from an upper layer, + * ifp must point it. + */ + if (ro->ro_rt == 0) { + if (ro == &ip6route) /* xxx kazu */ + rtalloc((struct route *)ro); + else + rtcalloc((struct route *)ro); + } + if (ro->ro_rt == 0) { + ip6stat.ip6s_noroute++; + error = EHOSTUNREACH; + /* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */ + goto bad; + } + ia = ifatoia6(ro->ro_rt->rt_ifa); + ifp = ro->ro_rt->rt_ifp; + ro->ro_rt->rt_use++; + if (ro->ro_rt->rt_flags & RTF_GATEWAY) + dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway; + m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ + + in6_ifstat_inc(ifp, ifs6_out_request); + + /* + * Check if there is the outgoing interface conflicts with + * the interface specified by ifi6_ifindex(if specified). + * Note that loopback interface is always okay. + * (this happens when we are sending packet toward my + * interface) + */ + if (opt && opt->ip6po_pktinfo + && opt->ip6po_pktinfo->ipi6_ifindex) { + if (!(ifp->if_flags & IFF_LOOPBACK) + && ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex) { + ip6stat.ip6s_noroute++; + in6_ifstat_inc(ifp, ifs6_out_discard); + error = EHOSTUNREACH; + goto bad; + } + } + + if (opt && opt->ip6po_hlim != -1) + ip6->ip6_hlim = opt->ip6po_hlim & 0xff; + } else { + /* Multicast */ + struct in6_multi *in6m; + + m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST; + + /* + * See if the caller provided any multicast options + */ + ifp = NULL; + if (im6o != NULL) { + ip6->ip6_hlim = im6o->im6o_multicast_hlim; + if (im6o->im6o_multicast_ifp != NULL) + ifp = im6o->im6o_multicast_ifp; + } else + ip6->ip6_hlim = ip6_defmcasthlim; + + /* + * See if the caller provided the outgoing interface + * as an ancillary data. + * Boundary check for ifindex is assumed to be already done. + */ + if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex) + ifp = ifindex2ifnet[opt->ip6po_pktinfo->ipi6_ifindex]; + + /* + * If the destination is a node-local scope multicast, + * the packet should be loop-backed only. + */ + if (IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst)) { + /* + * If the outgoing interface is already specified, + * it should be a loopback interface. + */ + if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) { + ip6stat.ip6s_badscope++; + error = ENETUNREACH; /* XXX: better error? */ + /* XXX correct ifp? */ + in6_ifstat_inc(ifp, ifs6_out_discard); + goto bad; + } else { + ifp = &loif[0]; + } + } + + if (opt && opt->ip6po_hlim != -1) + ip6->ip6_hlim = opt->ip6po_hlim & 0xff; + + /* + * If caller did not provide an interface lookup a + * default in the routing table. This is either a + * default for the speicfied group (i.e. a host + * route), or a multicast default (a route for the + * ``net'' ff00::/8). + */ + if (ifp == NULL) { + if (ro->ro_rt == 0) { + ro->ro_rt = rtalloc1((struct sockaddr *) + &ro->ro_dst, 0, 0UL); + } + if (ro->ro_rt == 0) { + ip6stat.ip6s_noroute++; + error = EHOSTUNREACH; + /* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */ + goto bad; + } + ia = ifatoia6(ro->ro_rt->rt_ifa); + ifp = ro->ro_rt->rt_ifp; + ro->ro_rt->rt_use++; + } + + if ((flags & IPV6_FORWARDING) == 0) + in6_ifstat_inc(ifp, ifs6_out_request); + in6_ifstat_inc(ifp, ifs6_out_mcast); + + /* + * Confirm that the outgoing interface supports multicast. + */ + if ((ifp->if_flags & IFF_MULTICAST) == 0) { + ip6stat.ip6s_noroute++; + in6_ifstat_inc(ifp, ifs6_out_discard); + error = ENETUNREACH; + goto bad; + } + IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m); + if (in6m != NULL && + (im6o == NULL || im6o->im6o_multicast_loop)) { + /* + * If we belong to the destination multicast group + * on the outgoing interface, and the caller did not + * forbid loopback, loop back a copy. + */ + ip6_mloopback(ifp, m, dst); + } + /* + * Multicasts with a hoplimit of zero may be looped back, + * above, but must not be transmitted on a network. + * Also, multicasts addressed to the loopback interface + * are not sent -- the above call to ip6_mloopback() will + * loop back a copy if this host actually belongs to the + * destination group on the loopback interface. + */ + if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) { + m_freem(m); + goto done; + } + } + + /* + * Fill the outgoing inteface to tell the upper layer + * to increment per-interface statistics. + */ + if (ifpp) + *ifpp = ifp; + + /* + * Determine path MTU. + */ + if (ro_pmtu != ro) { + /* The first hop and the final destination may differ. */ + struct sockaddr_in6 *sin6_fin = + (struct sockaddr_in6 *)&ro_pmtu->ro_dst; + if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || + !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr, + &finaldst))) { + RTFREE(ro_pmtu->ro_rt); + ro_pmtu->ro_rt = (struct rtentry *)0; + } + if (ro_pmtu->ro_rt == 0) { + bzero(sin6_fin, sizeof(*sin6_fin)); + sin6_fin->sin6_family = AF_INET6; + sin6_fin->sin6_len = sizeof(struct sockaddr_in6); + sin6_fin->sin6_addr = finaldst; + + rtcalloc((struct route *)ro_pmtu); + } + } + if (ro_pmtu->ro_rt != NULL) { + u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu; + + mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; + if (mtu > ifmtu) { + /* + * The MTU on the route is larger than the MTU on + * the interface! This shouldn't happen, unless the + * MTU of the interface has been changed after the + * interface was brought up. Change the MTU in the + * route to match the interface MTU (as long as the + * field isn't locked). + */ + mtu = ifmtu; + if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0) + ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */ + } + } else { + mtu = nd_ifinfo[ifp->if_index].linkmtu; + } + + /* + * Fake link-local scope-class addresses + */ + if ((ifp->if_flags & IFF_LOOPBACK) == 0) { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; + } + +#ifdef IPV6FIREWALL + /* + * Check with the firewall... + */ + if (ip6_fw_chk_ptr) { + u_short port = 0; + /* If ipfw says divert, we have to just drop packet */ + if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) { + m_freem(m); + goto done; + } + if (!m) { + error = EACCES; + goto done; + } + } +#endif + + /* + * If the outgoing packet contains a hop-by-hop options header, + * it must be examined and processed even by the source node. + * (RFC 2460, section 4.) + */ + if (exthdrs.ip6e_hbh) { + struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, + struct ip6_hbh *); + u_int32_t dummy1; /* XXX unused */ + u_int32_t dummy2; /* XXX unused */ + + /* + * XXX: if we have to send an ICMPv6 error to the sender, + * we need the M_LOOP flag since icmp6_error() expects + * the IPv6 and the hop-by-hop options header are + * continuous unless the flag is set. + */ + m->m_flags |= M_LOOP; + m->m_pkthdr.rcvif = ifp; + if (ip6_process_hopopts(m, + (u_int8_t *)(hbh + 1), + ((hbh->ip6h_len + 1) << 3) - + sizeof(struct ip6_hbh), + &dummy1, &dummy2) < 0) { + /* m was already freed at this point */ + error = EINVAL;/* better error? */ + goto done; + } + m->m_flags &= ~M_LOOP; /* XXX */ + m->m_pkthdr.rcvif = NULL; + } + + /* + * Send the packet to the outgoing interface. + * If necessary, do IPv6 fragmentation before sending. + */ + tlen = m->m_pkthdr.len; + if (tlen <= mtu +#ifdef notyet + /* + * On any link that cannot convey a 1280-octet packet in one piece, + * link-specific fragmentation and reassembly must be provided at + * a layer below IPv6. [RFC 2460, sec.5] + * Thus if the interface has ability of link-level fragmentation, + * we can just send the packet even if the packet size is + * larger than the link's MTU. + * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet... + */ + + || ifp->if_flags & IFF_FRAGMENTABLE +#endif + ) + { +#if defined(__NetBSD__) && defined(IFA_STATS) + if (IFA_STATS) { + struct in6_ifaddr *ia6; + ip6 = mtod(m, struct ip6_hdr *); + ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); + if (ia6) { + ia->ia_ifa.ifa_data.ifad_outbytes += + m->m_pkthdr.len; + } + } +#endif + error = nd6_output(ifp, m, dst, ro->ro_rt); + goto done; + } else if (mtu < IPV6_MMTU) { + /* + * note that path MTU is never less than IPV6_MMTU + * (see icmp6_input). + */ + error = EMSGSIZE; + in6_ifstat_inc(ifp, ifs6_out_fragfail); + goto bad; + } else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */ + error = EMSGSIZE; + in6_ifstat_inc(ifp, ifs6_out_fragfail); + goto bad; + } else { + struct mbuf **mnext, *m_frgpart; + struct ip6_frag *ip6f; + u_int32_t id = htonl(ip6_id++); + u_char nextproto; + + /* + * Too large for the destination or interface; + * fragment if possible. + * Must be able to put at least 8 bytes per fragment. + */ + hlen = unfragpartlen; + if (mtu > IPV6_MAXPACKET) + mtu = IPV6_MAXPACKET; + len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; + if (len < 8) { + error = EMSGSIZE; + in6_ifstat_inc(ifp, ifs6_out_fragfail); + goto bad; + } + + mnext = &m->m_nextpkt; + + /* + * Change the next header field of the last header in the + * unfragmentable part. + */ + if (exthdrs.ip6e_rthdr) { + nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *); + *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT; + } else if (exthdrs.ip6e_dest1) { + nextproto = *mtod(exthdrs.ip6e_dest1, u_char *); + *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT; + } else if (exthdrs.ip6e_hbh) { + nextproto = *mtod(exthdrs.ip6e_hbh, u_char *); + *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT; + } else { + nextproto = ip6->ip6_nxt; + ip6->ip6_nxt = IPPROTO_FRAGMENT; + } + + /* + * Loop through length of segment after first fragment, + * make new header and copy data of each part and link onto chain. + */ + m0 = m; + for (off = hlen; off < tlen; off += len) { + MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (!m) { + error = ENOBUFS; + ip6stat.ip6s_odropped++; + goto sendorfree; + } + m->m_flags = m0->m_flags & M_COPYFLAGS; + *mnext = m; + mnext = &m->m_nextpkt; + m->m_data += max_linkhdr; + mhip6 = mtod(m, struct ip6_hdr *); + *mhip6 = *ip6; + m->m_len = sizeof(*mhip6); + error = ip6_insertfraghdr(m0, m, hlen, &ip6f); + if (error) { + ip6stat.ip6s_odropped++; + goto sendorfree; + } + ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7)); + if (off + len >= tlen) + len = tlen - off; + else + ip6f->ip6f_offlg |= IP6F_MORE_FRAG; + mhip6->ip6_plen = htons((u_short)(len + hlen + + sizeof(*ip6f) - + sizeof(struct ip6_hdr))); + if ((m_frgpart = m_copy(m0, off, len)) == 0) { + error = ENOBUFS; + ip6stat.ip6s_odropped++; + goto sendorfree; + } + m_cat(m, m_frgpart); + m->m_pkthdr.len = len + hlen + sizeof(*ip6f); + m->m_pkthdr.rcvif = (struct ifnet *)0; + ip6f->ip6f_reserved = 0; + ip6f->ip6f_ident = id; + ip6f->ip6f_nxt = nextproto; + ip6stat.ip6s_ofragments++; + in6_ifstat_inc(ifp, ifs6_out_fragcreat); + } + + in6_ifstat_inc(ifp, ifs6_out_fragok); + } + + /* + * Remove leading garbages. + */ +sendorfree: + m = m0->m_nextpkt; + m0->m_nextpkt = 0; + m_freem(m0); + for (m0 = m; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; + if (error == 0) { +#if defined(__NetBSD__) && defined(IFA_STATS) + if (IFA_STATS) { + struct in6_ifaddr *ia6; + ip6 = mtod(m, struct ip6_hdr *); + ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); + if (ia6) { + ia->ia_ifa.ifa_data.ifad_outbytes += + m->m_pkthdr.len; + } + } +#endif + error = nd6_output(ifp, m, dst, ro->ro_rt); + } else + m_freem(m); + } + + if (error == 0) + ip6stat.ip6s_fragmented++; + +done: + if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */ + RTFREE(ro->ro_rt); + } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) { + RTFREE(ro_pmtu->ro_rt); + } + +#ifdef IPSEC + if (sp != NULL) + key_freesp(sp); +#endif /* IPSEC */ + + return(error); + +freehdrs: + m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */ + m_freem(exthdrs.ip6e_dest1); + m_freem(exthdrs.ip6e_rthdr); + m_freem(exthdrs.ip6e_dest2); + /* fall through */ +bad: + m_freem(m); + goto done; +} + +static int +ip6_copyexthdr(mp, hdr, hlen) + struct mbuf **mp; + caddr_t hdr; + int hlen; +{ + struct mbuf *m; + + if (hlen > MCLBYTES) + return(ENOBUFS); /* XXX */ + + MGET(m, M_DONTWAIT, MT_DATA); + if (!m) + return(ENOBUFS); + + if (hlen > MLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + return(ENOBUFS); + } + } + m->m_len = hlen; + if (hdr) + bcopy(hdr, mtod(m, caddr_t), hlen); + + *mp = m; + return(0); +} + +/* + * Insert jumbo payload option. + */ +static int +ip6_insert_jumboopt(exthdrs, plen) + struct ip6_exthdrs *exthdrs; + u_int32_t plen; +{ + struct mbuf *mopt; + u_char *optbuf; + +#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ + + /* + * If there is no hop-by-hop options header, allocate new one. + * If there is one but it doesn't have enough space to store the + * jumbo payload option, allocate a cluster to store the whole options. + * Otherwise, use it to store the options. + */ + if (exthdrs->ip6e_hbh == 0) { + MGET(mopt, M_DONTWAIT, MT_DATA); + if (mopt == 0) + return(ENOBUFS); + mopt->m_len = JUMBOOPTLEN; + optbuf = mtod(mopt, u_char *); + optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */ + exthdrs->ip6e_hbh = mopt; + } else { + struct ip6_hbh *hbh; + + mopt = exthdrs->ip6e_hbh; + if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { + caddr_t oldoptp = mtod(mopt, caddr_t); + int oldoptlen = mopt->m_len; + + if (mopt->m_flags & M_EXT) + return(ENOBUFS); /* XXX */ + MCLGET(mopt, M_DONTWAIT); + if ((mopt->m_flags & M_EXT) == 0) + return(ENOBUFS); + + bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen); + optbuf = mtod(mopt, caddr_t) + oldoptlen; + mopt->m_len = oldoptlen + JUMBOOPTLEN; + } else { + optbuf = mtod(mopt, u_char *) + mopt->m_len; + mopt->m_len += JUMBOOPTLEN; + } + optbuf[0] = IP6OPT_PADN; + optbuf[1] = 1; + + /* + * Adjust the header length according to the pad and + * the jumbo payload option. + */ + hbh = mtod(mopt, struct ip6_hbh *); + hbh->ip6h_len += (JUMBOOPTLEN >> 3); + } + + /* fill in the option. */ + optbuf[2] = IP6OPT_JUMBO; + optbuf[3] = 4; + *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN); + + /* finally, adjust the packet header length */ + exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; + + return(0); +#undef JUMBOOPTLEN +} + +/* + * Insert fragment header and copy unfragmentable header portions. + */ +static int +ip6_insertfraghdr(m0, m, hlen, frghdrp) + struct mbuf *m0, *m; + int hlen; + struct ip6_frag **frghdrp; +{ + struct mbuf *n, *mlast; + + if (hlen > sizeof(struct ip6_hdr)) { + n = m_copym(m0, sizeof(struct ip6_hdr), + hlen - sizeof(struct ip6_hdr), M_DONTWAIT); + if (n == 0) + return(ENOBUFS); + m->m_next = n; + } else + n = m; + + /* Search for the last mbuf of unfragmentable part. */ + for (mlast = n; mlast->m_next; mlast = mlast->m_next) + ; + + if ((mlast->m_flags & M_EXT) == 0 && + M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) { + /* use the trailing space of the last mbuf for the fragment hdr */ + *frghdrp = + (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len); + mlast->m_len += sizeof(struct ip6_frag); + m->m_pkthdr.len += sizeof(struct ip6_frag); + } else { + /* allocate a new mbuf for the fragment header */ + struct mbuf *mfrg; + + MGET(mfrg, M_DONTWAIT, MT_DATA); + if (mfrg == 0) + return(ENOBUFS); + mfrg->m_len = sizeof(struct ip6_frag); + *frghdrp = mtod(mfrg, struct ip6_frag *); + mlast->m_next = mfrg; + } + + return(0); +} + +/* + * IP6 socket option processing. + */ +int +ip6_ctloutput(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + int privileged; + register struct inpcb *in6p = sotoinpcb(so); + int error, optval; + int level, op, optname; + int optlen; + struct proc *p; + + if (sopt) { + level = sopt->sopt_level; + op = sopt->sopt_dir; + optname = sopt->sopt_name; + optlen = sopt->sopt_valsize; + p = sopt->sopt_p; + } else { + panic("ip6_ctloutput: arg soopt is NULL"); + } + error = optval = 0; + + privileged = (p == 0 || suser(p)) ? 0 : 1; + + if (level == IPPROTO_IPV6) { + switch (op) { + case SOPT_SET: + switch (optname) { + case IPV6_PKTOPTIONS: + { + struct mbuf *m; + + error = soopt_getm(sopt, &m); /* XXX */ + if (error != NULL) + break; + error = soopt_mcopyin(sopt, m); /* XXX */ + if (error != NULL) + break; + return (ip6_pcbopts(&in6p->in6p_outputopts, + m, so, sopt)); + } + case IPV6_HOPOPTS: + case IPV6_DSTOPTS: + if (!privileged) { + error = EPERM; + break; + } + /* fall through */ + case IPV6_UNICAST_HOPS: + case IPV6_RECVOPTS: + case IPV6_RECVRETOPTS: + case IPV6_RECVDSTADDR: + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_RTHDR: + case IPV6_CHECKSUM: + case IPV6_FAITH: + case IPV6_BINDV6ONLY: + if (optlen != sizeof(int)) + error = EINVAL; + else { + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + switch (optname) { + + case IPV6_UNICAST_HOPS: + if (optval < -1 || optval >= 256) + error = EINVAL; + else { + /* -1 = kernel default */ + in6p->in6p_hops = optval; + if ((in6p->in6p_vflag & + INP_IPV4) != 0) + in6p->inp_ip_ttl = optval; + } + break; +#define OPTSET(bit) \ + if (optval) \ + in6p->in6p_flags |= bit; \ + else \ + in6p->in6p_flags &= ~bit; + + case IPV6_RECVOPTS: + OPTSET(IN6P_RECVOPTS); + break; + + case IPV6_RECVRETOPTS: + OPTSET(IN6P_RECVRETOPTS); + break; + + case IPV6_RECVDSTADDR: + OPTSET(IN6P_RECVDSTADDR); + break; + + case IPV6_PKTINFO: + OPTSET(IN6P_PKTINFO); + break; + + case IPV6_HOPLIMIT: + OPTSET(IN6P_HOPLIMIT); + break; + + case IPV6_HOPOPTS: + OPTSET(IN6P_HOPOPTS); + break; + + case IPV6_DSTOPTS: + OPTSET(IN6P_DSTOPTS); + break; + + case IPV6_RTHDR: + OPTSET(IN6P_RTHDR); + break; + + case IPV6_CHECKSUM: + in6p->in6p_cksum = optval; + break; + + case IPV6_FAITH: + OPTSET(IN6P_FAITH); + break; + + case IPV6_BINDV6ONLY: + OPTSET(IN6P_BINDV6ONLY); + break; + } + } + break; +#undef OPTSET + + case IPV6_MULTICAST_IF: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: + case IPV6_JOIN_GROUP: + case IPV6_LEAVE_GROUP: + { + struct mbuf *m; + if (sopt->sopt_valsize > MLEN) { + error = EMSGSIZE; + break; + } + /* XXX */ + MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER); + if (m == 0) { + error = ENOBUFS; + break; + } + m->m_len = sopt->sopt_valsize; + error = sooptcopyin(sopt, mtod(m, char *), + m->m_len, m->m_len); + error = ip6_setmoptions(sopt->sopt_name, + &in6p->in6p_moptions, + m); + (void)m_free(m); + } + break; + + case IPV6_PORTRANGE: + error = sooptcopyin(sopt, &optval, sizeof optval, + sizeof optval); + if (error) + break; + + switch (optval) { + case IPV6_PORTRANGE_DEFAULT: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + break; + + case IPV6_PORTRANGE_HIGH: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags |= IN6P_HIGHPORT; + break; + + case IPV6_PORTRANGE_LOW: + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + in6p->in6p_flags |= IN6P_LOWPORT; + break; + + default: + error = EINVAL; + break; + } + break; + +#ifdef IPSEC + case IPV6_IPSEC_POLICY: + { + caddr_t req = NULL; + struct mbuf *m; + + if (error = soopt_getm(sopt, &m)) /* XXX */ + break; + if (error = soopt_mcopyin(sopt, m)) /* XXX */ + break; + if (m != 0) + req = mtod(m, caddr_t); + error = ipsec6_set_policy(in6p, optname, req, + privileged); + m_freem(m); + } + break; +#endif /* IPSEC */ + +#ifdef IPV6FIREWALL + case IPV6_FW_ADD: + case IPV6_FW_DEL: + case IPV6_FW_FLUSH: + case IPV6_FW_ZERO: + { + struct mbuf *m; + struct mbuf **mp = &m; + + if (ip6_fw_ctl_ptr == NULL) + return EINVAL; + if (error = soopt_getm(sopt, &m)) /* XXX */ + break; + if (error = soopt_mcopyin(sopt, m)) /* XXX */ + break; + error = (*ip6_fw_ctl_ptr)(optname, mp); + m = *mp; + } + break; +#endif + + default: + error = ENOPROTOOPT; + break; + } + break; + + case SOPT_GET: + switch (optname) { + + case IPV6_OPTIONS: + case IPV6_RETOPTS: + error = ENOPROTOOPT; + break; + + case IPV6_PKTOPTIONS: + if (in6p->in6p_options) { + error = soopt_mcopyout(sopt, + in6p->in6p_options); + } else + sopt->sopt_valsize = 0; + break; + + case IPV6_HOPOPTS: + case IPV6_DSTOPTS: + if (!privileged) { + error = EPERM; + break; + } + /* fall through */ + case IPV6_UNICAST_HOPS: + case IPV6_RECVOPTS: + case IPV6_RECVRETOPTS: + case IPV6_RECVDSTADDR: + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_RTHDR: + case IPV6_CHECKSUM: + case IPV6_FAITH: + case IPV6_BINDV6ONLY: + switch (optname) { + + case IPV6_UNICAST_HOPS: + optval = in6p->in6p_hops; + break; + +#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0) + + case IPV6_RECVOPTS: + optval = OPTBIT(IN6P_RECVOPTS); + break; + + case IPV6_RECVRETOPTS: + optval = OPTBIT(IN6P_RECVRETOPTS); + break; + + case IPV6_RECVDSTADDR: + optval = OPTBIT(IN6P_RECVDSTADDR); + break; + + case IPV6_PKTINFO: + optval = OPTBIT(IN6P_PKTINFO); + break; + + case IPV6_HOPLIMIT: + optval = OPTBIT(IN6P_HOPLIMIT); + break; + + case IPV6_HOPOPTS: + optval = OPTBIT(IN6P_HOPOPTS); + break; + + case IPV6_DSTOPTS: + optval = OPTBIT(IN6P_DSTOPTS); + break; + + case IPV6_RTHDR: + optval = OPTBIT(IN6P_RTHDR); + break; + + case IPV6_CHECKSUM: + optval = in6p->in6p_cksum; + break; + + case IPV6_FAITH: + optval = OPTBIT(IN6P_FAITH); + break; + + case IPV6_BINDV6ONLY: + optval = OPTBIT(IN6P_BINDV6ONLY); + break; + + case IPV6_PORTRANGE: + { + int flags; + + flags = in6p->in6p_flags; + if (flags & IN6P_HIGHPORT) + optval = IPV6_PORTRANGE_HIGH; + else if (flags & IN6P_LOWPORT) + optval = IPV6_PORTRANGE_LOW; + else + optval = 0; + break; + } + } + error = sooptcopyout(sopt, &optval, + sizeof optval); + break; + + case IPV6_MULTICAST_IF: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: + case IPV6_JOIN_GROUP: + case IPV6_LEAVE_GROUP: + { + struct mbuf *m; + error = ip6_getmoptions(sopt->sopt_name, + in6p->in6p_moptions, &m); + if (error == 0) + error = sooptcopyout(sopt, + mtod(m, char *), m->m_len); + m_freem(m); + } + break; + +#ifdef IPSEC + case IPV6_IPSEC_POLICY: + { + caddr_t req = NULL; + int len = 0; + struct mbuf *m; + struct mbuf **mp = &m; + + if (m != 0) { + req = mtod(m, caddr_t); + len = m->m_len; + } + error = ipsec6_get_policy(in6p, req, mp); + if (error == 0) + error = soopt_mcopyout(sopt, m); /*XXX*/ + m_freem(m); + break; + } +#endif /* IPSEC */ + +#ifdef IPV6FIREWALL + case IPV6_FW_GET: + { + struct mbuf *m; + struct mbuf **mp = &m; + + if (ip6_fw_ctl_ptr == NULL) + { + return EINVAL; + } + error = (*ip6_fw_ctl_ptr)(optname, mp); + if (error == 0) + error = soopt_mcopyout(sopt, m); /* XXX */ + if (m) + m_freem(m); + } + break; +#endif + + default: + error = ENOPROTOOPT; + break; + } + break; + } + } else { + error = EINVAL; + } + return(error); +} + +/* + * Set up IP6 options in pcb for insertion in output packets. + * Store in mbuf with pointer in pcbopt, adding pseudo-option + * with destination address if source routed. + */ +static int +ip6_pcbopts(pktopt, m, so, sopt) + struct ip6_pktopts **pktopt; + register struct mbuf *m; + struct socket *so; + struct sockopt *sopt; +{ + register struct ip6_pktopts *opt = *pktopt; + int error = 0; + struct proc *p = sopt->sopt_p; + int priv = 0; + + /* turn off any old options. */ + if (opt) { + if (opt->ip6po_m) + (void)m_free(opt->ip6po_m); + } else + opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); + *pktopt = 0; + + if (!m || m->m_len == 0) { + /* + * Only turning off any previous options. + */ + if (opt) + free(opt, M_IP6OPT); + if (m) + (void)m_free(m); + return(0); + } + + /* set options specified by user. */ + if (p && !suser(p)) + priv = 1; + if ((error = ip6_setpktoptions(m, opt, priv)) != 0) { + (void)m_free(m); + return(error); + } + *pktopt = opt; + return(0); +} + +/* + * Set the IP6 multicast options in response to user setsockopt(). + */ +static int +ip6_setmoptions(optname, im6op, m) + int optname; + struct ip6_moptions **im6op; + struct mbuf *m; +{ + int error = 0; + u_int loop, ifindex; + struct ipv6_mreq *mreq; + struct ifnet *ifp; + struct ip6_moptions *im6o = *im6op; + struct route_in6 ro; + struct sockaddr_in6 *dst; + struct in6_multi_mship *imm; + struct proc *p = curproc; /* XXX */ + + if (im6o == NULL) { + /* + * No multicast option buffer attached to the pcb; + * allocate one and initialize to default values. + */ + im6o = (struct ip6_moptions *) + malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK); + + if (im6o == NULL) + return(ENOBUFS); + *im6op = im6o; + im6o->im6o_multicast_ifp = NULL; + im6o->im6o_multicast_hlim = ip6_defmcasthlim; + im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP; + LIST_INIT(&im6o->im6o_memberships); + } + + switch (optname) { + + case IPV6_MULTICAST_IF: + /* + * Select the interface for outgoing multicast packets. + */ + if (m == NULL || m->m_len != sizeof(u_int)) { + error = EINVAL; + break; + } + ifindex = *(mtod(m, u_int *)); + if (ifindex < 0 || if_index < ifindex) { + error = ENXIO; /* XXX EINVAL? */ + break; + } + ifp = ifindex2ifnet[ifindex]; + if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { + error = EADDRNOTAVAIL; + break; + } + im6o->im6o_multicast_ifp = ifp; + break; + + case IPV6_MULTICAST_HOPS: + { + /* + * Set the IP6 hoplimit for outgoing multicast packets. + */ + int optval; + if (m == NULL || m->m_len != sizeof(int)) { + error = EINVAL; + break; + } + optval = *(mtod(m, u_int *)); + if (optval < -1 || optval >= 256) + error = EINVAL; + else if (optval == -1) + im6o->im6o_multicast_hlim = ip6_defmcasthlim; + else + im6o->im6o_multicast_hlim = optval; + break; + } + + case IPV6_MULTICAST_LOOP: + /* + * Set the loopback flag for outgoing multicast packets. + * Must be zero or one. + */ + if (m == NULL || m->m_len != sizeof(u_int) || + (loop = *(mtod(m, u_int *))) > 1) { + error = EINVAL; + break; + } + im6o->im6o_multicast_loop = loop; + break; + + case IPV6_JOIN_GROUP: + /* + * Add a multicast group membership. + * Group must be a valid IP6 multicast address. + */ + if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { + error = EINVAL; + break; + } + mreq = mtod(m, struct ipv6_mreq *); + if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { + /* + * We use the unspecified address to specify to accept + * all multicast addresses. Only super user is allowed + * to do this. + */ + if (suser(p)) { + error = EACCES; + break; + } + } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { + error = EINVAL; + break; + } + + /* + * If the interface is specified, validate it. + */ + if (mreq->ipv6mr_interface < 0 + || if_index < mreq->ipv6mr_interface) { + error = ENXIO; /* XXX EINVAL? */ + break; + } + /* + * If no interface was explicitly specified, choose an + * appropriate one according to the given multicast address. + */ + if (mreq->ipv6mr_interface == 0) { + /* + * If the multicast address is in node-local scope, + * the interface should be a loopback interface. + * Otherwise, look up the routing table for the + * address, and choose the outgoing interface. + * XXX: is it a good approach? + */ + if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) { + ifp = &loif[0]; + } else { + ro.ro_rt = NULL; + dst = (struct sockaddr_in6 *)&ro.ro_dst; + bzero(dst, sizeof(*dst)); + dst->sin6_len = sizeof(struct sockaddr_in6); + dst->sin6_family = AF_INET6; + dst->sin6_addr = mreq->ipv6mr_multiaddr; + rtalloc((struct route *)&ro); + if (ro.ro_rt == NULL) { + error = EADDRNOTAVAIL; + break; + } + ifp = ro.ro_rt->rt_ifp; + rtfree(ro.ro_rt); + } + } else + ifp = ifindex2ifnet[mreq->ipv6mr_interface]; + + /* + * See if we found an interface, and confirm that it + * supports multicast + */ + if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { + error = EADDRNOTAVAIL; + break; + } + /* + * Put interface index into the multicast address, + * if the address has link-local scope. + */ + if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { + mreq->ipv6mr_multiaddr.s6_addr16[1] + = htons(mreq->ipv6mr_interface); + } + /* + * See if the membership already exists. + */ + for (imm = im6o->im6o_memberships.lh_first; + imm != NULL; imm = imm->i6mm_chain.le_next) + if (imm->i6mm_maddr->in6m_ifp == ifp && + IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, + &mreq->ipv6mr_multiaddr)) + break; + if (imm != NULL) { + error = EADDRINUSE; + break; + } + /* + * Everything looks good; add a new record to the multicast + * address list for the given interface. + */ + imm = malloc(sizeof(*imm), M_IPMADDR, M_WAITOK); + if (imm == NULL) { + error = ENOBUFS; + break; + } + if ((imm->i6mm_maddr = + in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) { + free(imm, M_IPMADDR); + break; + } + LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); + break; + + case IPV6_LEAVE_GROUP: + /* + * Drop a multicast group membership. + * Group must be a valid IP6 multicast address. + */ + if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { + error = EINVAL; + break; + } + mreq = mtod(m, struct ipv6_mreq *); + if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { + if (suser(p)) { + error = EACCES; + break; + } + } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { + error = EINVAL; + break; + } + /* + * If an interface address was specified, get a pointer + * to its ifnet structure. + */ + if (mreq->ipv6mr_interface < 0 + || if_index < mreq->ipv6mr_interface) { + error = ENXIO; /* XXX EINVAL? */ + break; + } + ifp = ifindex2ifnet[mreq->ipv6mr_interface]; + /* + * Put interface index into the multicast address, + * if the address has link-local scope. + */ + if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { + mreq->ipv6mr_multiaddr.s6_addr16[1] + = htons(mreq->ipv6mr_interface); + } + /* + * Find the membership in the membership list. + */ + for (imm = im6o->im6o_memberships.lh_first; + imm != NULL; imm = imm->i6mm_chain.le_next) { + if ((ifp == NULL || + imm->i6mm_maddr->in6m_ifp == ifp) && + IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, + &mreq->ipv6mr_multiaddr)) + break; + } + if (imm == NULL) { + /* Unable to resolve interface */ + error = EADDRNOTAVAIL; + break; + } + /* + * Give up the multicast address record to which the + * membership points. + */ + LIST_REMOVE(imm, i6mm_chain); + in6_delmulti(imm->i6mm_maddr); + free(imm, M_IPMADDR); + break; + + default: + error = EOPNOTSUPP; + break; + } + + /* + * If all options have default values, no need to keep the mbuf. + */ + if (im6o->im6o_multicast_ifp == NULL && + im6o->im6o_multicast_hlim == ip6_defmcasthlim && + im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && + im6o->im6o_memberships.lh_first == NULL) { + free(*im6op, M_IPMOPTS); + *im6op = NULL; + } + + return(error); +} + +/* + * Return the IP6 multicast options in response to user getsockopt(). + */ +static int +ip6_getmoptions(optname, im6o, mp) + int optname; + register struct ip6_moptions *im6o; + register struct mbuf **mp; +{ + u_int *hlim, *loop, *ifindex; + + *mp = m_get(M_WAIT, MT_HEADER); /*XXX*/ + + switch (optname) { + + case IPV6_MULTICAST_IF: + ifindex = mtod(*mp, u_int *); + (*mp)->m_len = sizeof(u_int); + if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) + *ifindex = 0; + else + *ifindex = im6o->im6o_multicast_ifp->if_index; + return(0); + + case IPV6_MULTICAST_HOPS: + hlim = mtod(*mp, u_int *); + (*mp)->m_len = sizeof(u_int); + if (im6o == NULL) + *hlim = ip6_defmcasthlim; + else + *hlim = im6o->im6o_multicast_hlim; + return(0); + + case IPV6_MULTICAST_LOOP: + loop = mtod(*mp, u_int *); + (*mp)->m_len = sizeof(u_int); + if (im6o == NULL) + *loop = ip6_defmcasthlim; + else + *loop = im6o->im6o_multicast_loop; + return(0); + + default: + return(EOPNOTSUPP); + } +} + +/* + * Discard the IP6 multicast options. + */ +void +ip6_freemoptions(im6o) + register struct ip6_moptions *im6o; +{ + struct in6_multi_mship *imm; + + if (im6o == NULL) + return; + + while ((imm = im6o->im6o_memberships.lh_first) != NULL) { + LIST_REMOVE(imm, i6mm_chain); + if (imm->i6mm_maddr) + in6_delmulti(imm->i6mm_maddr); + free(imm, M_IPMADDR); + } + free(im6o, M_IPMOPTS); +} + +/* + * Set IPv6 outgoing packet options based on advanced API. + */ +int +ip6_setpktoptions(control, opt, priv) + struct mbuf *control; + struct ip6_pktopts *opt; + int priv; +{ + register struct cmsghdr *cm = 0; + + if (control == 0 || opt == 0) + return(EINVAL); + + bzero(opt, sizeof(*opt)); + opt->ip6po_hlim = -1; /* -1 means to use default hop limit */ + + /* + * XXX: Currently, we assume all the optional information is stored + * in a single mbuf. + */ + if (control->m_next) + return(EINVAL); + + opt->ip6po_m = control; + + for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len), + control->m_len -= CMSG_ALIGN(cm->cmsg_len)) { + cm = mtod(control, struct cmsghdr *); + if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) + return(EINVAL); + if (cm->cmsg_level != IPPROTO_IPV6) + continue; + + switch(cm->cmsg_type) { + case IPV6_PKTINFO: + if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) + return(EINVAL); + opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm); + if (opt->ip6po_pktinfo->ipi6_ifindex && + IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr)) + opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] = + htons(opt->ip6po_pktinfo->ipi6_ifindex); + + if (opt->ip6po_pktinfo->ipi6_ifindex > if_index + || opt->ip6po_pktinfo->ipi6_ifindex < 0) { + return(ENXIO); + } + + if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) { + struct ifaddr *ia; + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = + opt->ip6po_pktinfo->ipi6_addr; + ia = ifa_ifwithaddr(sin6tosa(&sin6)); + if (ia == NULL || + (opt->ip6po_pktinfo->ipi6_ifindex && + (ia->ifa_ifp->if_index != + opt->ip6po_pktinfo->ipi6_ifindex))) { + return(EADDRNOTAVAIL); + } + /* + * Check if the requested source address is + * indeed a unicast address assigned to the + * node. + */ + if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr)) + return(EADDRNOTAVAIL); + } + break; + + case IPV6_HOPLIMIT: + if (cm->cmsg_len != CMSG_LEN(sizeof(int))) + return(EINVAL); + + opt->ip6po_hlim = *(int *)CMSG_DATA(cm); + if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255) + return(EINVAL); + break; + + case IPV6_NEXTHOP: + if (!priv) + return(EPERM); + if (cm->cmsg_len < sizeof(u_char) || + cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm))) + return(EINVAL); + + opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm); + + break; + + case IPV6_HOPOPTS: + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh))) + return(EINVAL); + opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm); + if (cm->cmsg_len != + CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3)) + return(EINVAL); + break; + + case IPV6_DSTOPTS: + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) + return(EINVAL); + + /* + * If there is no routing header yet, the destination + * options header should be put on the 1st part. + * Otherwise, the header should be on the 2nd part. + * (See RFC 2460, section 4.1) + */ + if (opt->ip6po_rthdr == NULL) { + opt->ip6po_dest1 = + (struct ip6_dest *)CMSG_DATA(cm); + if (cm->cmsg_len != + CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1) + << 3)) + return(EINVAL); + } else { + opt->ip6po_dest2 = + (struct ip6_dest *)CMSG_DATA(cm); + if (cm->cmsg_len != + CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1) + << 3)) + return(EINVAL); + } + break; + + case IPV6_RTHDR: + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) + return(EINVAL); + opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm); + if (cm->cmsg_len != + CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3)) + return(EINVAL); + switch(opt->ip6po_rthdr->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + if (opt->ip6po_rthdr->ip6r_segleft == 0) + return(EINVAL); + break; + default: + return(EINVAL); + } + break; + + default: + return(ENOPROTOOPT); + } + } + + return(0); +} + +/* + * Routine called from ip6_output() to loop back a copy of an IP6 multicast + * packet to the input queue of a specified interface. Note that this + * calls the output routine of the loopback "driver", but with an interface + * pointer that might NOT be &loif -- easier than replicating that code here. + */ +void +ip6_mloopback(ifp, m, dst) + struct ifnet *ifp; + register struct mbuf *m; + register struct sockaddr_in6 *dst; +{ + struct mbuf *copym; + + copym = m_copy(m, 0, M_COPYALL); + if (copym != NULL) { + (void)if_simloop(ifp, copym, (struct sockaddr *)dst, NULL); + } +} + +/* + * Chop IPv6 header off from the payload. + */ +static int +ip6_splithdr(m, exthdrs) + struct mbuf *m; + struct ip6_exthdrs *exthdrs; +{ + struct mbuf *mh; + struct ip6_hdr *ip6; + + ip6 = mtod(m, struct ip6_hdr *); + if (m->m_len > sizeof(*ip6)) { + MGETHDR(mh, M_DONTWAIT, MT_HEADER); + if (mh == 0) { + m_freem(m); + return ENOBUFS; + } + M_COPY_PKTHDR(mh, m); + MH_ALIGN(mh, sizeof(*ip6)); + m->m_flags &= ~M_PKTHDR; + m->m_len -= sizeof(*ip6); + m->m_data += sizeof(*ip6); + mh->m_next = m; + m = mh; + m->m_len = sizeof(*ip6); + bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6)); + } + exthdrs->ip6e_ip6 = m; + return 0; +} + +/* + * Compute IPv6 extension header length. + */ +int +ip6_optlen(in6p) + struct in6pcb *in6p; +{ + int len; + + if (!in6p->in6p_outputopts) + return 0; + + len = 0; +#define elen(x) \ + (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0) + + len += elen(in6p->in6p_outputopts->ip6po_hbh); + len += elen(in6p->in6p_outputopts->ip6po_dest1); + len += elen(in6p->in6p_outputopts->ip6po_rthdr); + len += elen(in6p->in6p_outputopts->ip6po_dest2); + return len; +#undef elen +} + diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h new file mode 100644 index 0000000..21df4f7 --- /dev/null +++ b/sys/netinet6/ip6_var.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_var.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET6_IP6_VAR_H_ +#define _NETINET6_IP6_VAR_H_ + +/* + * IP6 reassembly queue structure. Each fragment + * being reassembled is attached to one of these structures. + */ +struct ip6q { + u_long ip6q_head; + u_short ip6q_len; + u_char ip6q_nxt; + u_char ip6q_hlim; + struct ip6asfrag *ip6q_down; + struct ip6asfrag *ip6q_up; + u_long ip6q_ident; + u_char ip6q_arrive; + u_char ip6q_ttl; + struct in6_addr ip6q_src, ip6q_dst; + struct ip6q *ip6q_next; + struct ip6q *ip6q_prev; + int ip6q_unfrglen; +}; + +struct ip6asfrag { + u_long ip6af_head; + u_short ip6af_len; + u_char ip6af_nxt; + u_char ip6af_hlim; + /* must not override the above members during reassembling */ + struct ip6asfrag *ip6af_down; + struct ip6asfrag *ip6af_up; + u_short ip6af_mff; + u_short ip6af_off; + struct mbuf *ip6af_m; + u_long ip6af_offset; /* offset where next header starts */ + u_short ip6af_frglen; /* fragmentable part length */ + u_char ip6af_x1[10]; +}; + +#define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m)) + +struct ip6_moptions { + struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */ + u_char im6o_multicast_hlim; /* hoplimit for outgoing multicasts */ + u_char im6o_multicast_loop; /* 1 >= hear sends if a member */ + LIST_HEAD(, in6_multi_mship) im6o_memberships; +}; + +/* + * Control options for outgoing packets + */ + +/* Routing header related info */ +struct ip6po_rhinfo { + struct ip6_rthdr *ip6po_rhi_rthdr; /* Routing header */ + struct route_in6 ip6po_rhi_route; /* Route to the 1st hop */ +}; +#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr +#define ip6po_route ip6po_rhinfo.ip6po_rhi_route + +struct ip6_pktopts { + struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */ + int ip6po_hlim; /* Hoplimit for outgoing packets */ + struct in6_pktinfo *ip6po_pktinfo; /* Outgoing IF/address information */ + struct sockaddr *ip6po_nexthop; /* Next-hop address */ + struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */ + struct ip6_dest *ip6po_dest1; /* Destination options header(1st part) */ + struct ip6po_rhinfo ip6po_rhinfo; /* Routing header related info. */ + struct ip6_dest *ip6po_dest2; /* Destination options header(2nd part) */ +}; + +struct ip6stat { + u_long ip6s_total; /* total packets received */ + u_long ip6s_tooshort; /* packet too short */ + u_long ip6s_toosmall; /* not enough data */ + u_long ip6s_fragments; /* fragments received */ + u_long ip6s_fragdropped; /* frags dropped(dups, out of space) */ + u_long ip6s_fragtimeout; /* fragments timed out */ + u_long ip6s_fragoverflow; /* fragments that exceeded limit */ + u_long ip6s_forward; /* packets forwarded */ + u_long ip6s_cantforward; /* packets rcvd for unreachable dest */ + u_long ip6s_redirectsent; /* packets forwarded on same net */ + u_long ip6s_delivered; /* datagrams delivered to upper level*/ + u_long ip6s_localout; /* total ip packets generated here */ + u_long ip6s_odropped; /* lost packets due to nobufs, etc. */ + u_long ip6s_reassembled; /* total packets reassembled ok */ + u_long ip6s_fragmented; /* datagrams sucessfully fragmented */ + u_long ip6s_ofragments; /* output fragments created */ + u_long ip6s_cantfrag; /* don't fragment flag was set, etc. */ + u_long ip6s_badoptions; /* error in option processing */ + u_long ip6s_noroute; /* packets discarded due to no route */ + u_long ip6s_badvers; /* ip6 version != 6 */ + u_long ip6s_rawout; /* total raw ip packets generated */ + u_long ip6s_badscope; /* scope error */ + u_long ip6s_notmember; /* don't join this multicast group */ + u_long ip6s_nxthist[256]; /* next header history */ + u_long ip6s_m1; /* one mbuf */ + u_long ip6s_m2m[32]; /* two or more mbuf */ + u_long ip6s_mext1; /* one ext mbuf */ + u_long ip6s_mext2m; /* two or more ext mbuf */ + u_long ip6s_exthdrtoolong; /* ext hdr are not continuous */ + u_long ip6s_nogif; /* no match gif found */ + u_long ip6s_toomanyhdr; /* discarded due to too many headers */ +}; + +#ifdef _KERNEL +/* flags passed to ip6_output as last parameter */ +#define IPV6_DADOUTPUT 0x01 /* DAD */ +#define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ + +extern struct ip6stat ip6stat; /* statistics */ +extern u_int32_t ip6_id; /* fragment identifier */ +extern int ip6_defhlim; /* default hop limit */ +extern int ip6_defmcasthlim; /* default multicast hop limit */ +extern int ip6_forwarding; /* act as router? */ +extern int ip6_forward_srcrt; /* forward src-routed? */ +extern int ip6_gif_hlim; /* Hop limit for gif encap packet */ +extern int ip6_use_deprecated; /* allow deprecated addr as source */ +extern int ip6_rr_prune; /* router renumbering prefix + * walk list every 5 sec. */ +extern int ip6_mapped_addr_on; + +extern struct socket *ip6_mrouter; /* multicast routing daemon */ +extern int ip6_sendredirects; /* send IP redirects when forwarding? */ +extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */ +extern int ip6_sourcecheck; /* Verify source interface */ +extern int ip6_sourcecheck_interval; /* Interval between log messages */ +extern int ip6_accept_rtadv; /* Acts as a host not a router */ +extern int ip6_keepfaith; /* Firewall Aided Internet Translator */ +extern int ip6_log_interval; +extern time_t ip6_log_time; +extern int ip6_hdrnestlimit; /* upper limit of # of extension headers */ +extern int ip6_dad_count; /* DupAddrDetectionTransmits */ + +extern u_int32_t ip6_flow_seq; +extern int ip6_auto_flowlabel; + +extern struct pr_usrreqs rip6_usrreqs; +struct sockopt; +struct inpcb; + +int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt)); + +void ip6_init __P((void)); +void ip6intr __P((void)); +void ip6_input __P((struct mbuf *)); +void ip6_freemoptions __P((struct ip6_moptions *)); +int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int)); +char * ip6_get_prevhdr __P((struct mbuf *, int)); +int ip6_mforward __P((struct ip6_hdr *, struct ifnet *, struct mbuf *)); +int ip6_process_hopopts __P((struct mbuf *, u_int8_t *, int, u_int32_t *, + u_int32_t *)); +void ip6_savecontrol __P((struct inpcb *, struct mbuf **, struct ip6_hdr *, + struct mbuf *)); +int ip6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); + +void ip6_forward __P((struct mbuf *, int)); + +void ip6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *)); +int ip6_output __P((struct mbuf *, struct ip6_pktopts *, + struct route_in6 *, int, + struct ip6_moptions *, struct ifnet **)); +int ip6_ctloutput __P((struct socket *, struct sockopt *sopt)); +int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int)); +int ip6_optlen __P((struct inpcb *)); + +int route6_input __P((struct mbuf **, int *, int)); + +void frag6_init __P((void)); +int frag6_input __P((struct mbuf **, int *, int)); +void frag6_slowtimo __P((void)); +void frag6_drain __P((void)); + +void rip6_init __P((void)); +int rip6_input __P((struct mbuf **mp, int *offp, int proto)); +int rip6_ctloutput __P((struct socket *so, struct sockopt *sopt)); +int rip6_output __P((struct mbuf *, ...)); +int rip6_usrreq __P((struct socket *, + int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *)); + +int dest6_input __P((struct mbuf **, int *, int)); +int none_input __P((struct mbuf **, int *, int)); +#endif /* _KERNEL */ + +#endif /* !_NETINET6_IP6_VAR_H_ */ diff --git a/sys/netinet6/ip6protosw.h b/sys/netinet6/ip6protosw.h new file mode 100644 index 0000000..bf3e4dc --- /dev/null +++ b/sys/netinet6/ip6protosw.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* BSDI protosw.h,v 2.3 1996/10/11 16:02:40 pjd Exp */ + +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)protosw.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _NETINET6_IP6PROTOSW_H_ +#define _NETINET6_IP6PROTOSW_H_ + +/* + * Protocol switch table for IPv6. + * All other definitions should refer to sys/protosw.h + */ + +struct mbuf; +struct sockaddr; +struct socket; +struct domain; +struct proc; +struct ip6_hdr; +struct pr_usrreqs; + +/* + * argument type for the last arg of pr_ctlinput(). + * should be consulted only with AF_INET6 family. + */ +struct ip6ctlparam { + struct mbuf *ip6c_m; /* start of mbuf chain */ + struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ + int ip6c_off; /* offset of the target proto header */ +}; + +struct ip6protosw { + int pr_type; /* socket type used for */ + struct domain *pr_domain; /* domain protocol a member of */ + short pr_protocol; /* protocol number */ + short pr_flags; /* see below */ + +/* protocol-protocol hooks */ + int (*pr_input) /* input to protocol (from below) */ + __P((struct mbuf **, int *, int)); + int (*pr_output) /* output to protocol (from above) */ + __P((struct mbuf *, ...)); + void (*pr_ctlinput) /* control input (from below) */ + __P((int, struct sockaddr *, void *)); + int (*pr_ctloutput) /* control output (from above) */ + __P((struct socket *, struct sockopt *)); + +/* user-protocol hook */ + int (*pr_usrreq) /* user request: see list below */ + __P((struct socket *, int, struct mbuf *, + struct mbuf *, struct mbuf *, struct proc *)); + +/* utility hooks */ + void (*pr_init) /* initialization hook */ + __P((void)); + + void (*pr_fasttimo) /* fast timeout (200ms) */ + __P((void)); + void (*pr_slowtimo) /* slow timeout (500ms) */ + __P((void)); + void (*pr_drain) /* flush any excess space possible */ + __P((void)); + struct pr_usrreqs *pr_usrreqs; /* supersedes pr_usrreq() */ +}; + +#endif /* !_NETINET6_IP6PROTOSW_H_ */ diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h new file mode 100644 index 0000000..6fdcba1 --- /dev/null +++ b/sys/netinet6/ipsec.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * IPsec controller part. + */ + +#ifndef _NETINET6_IPSEC_H_ +#define _NETINET6_IPSEC_H_ + +#include +#include + +#ifdef KERNEL + +/* + * Security Policy Index + * NOTE: Encure to be same address family and upper layer protocol. + * NOTE: ul_proto, port number, uid, gid: + * ANY: reserved for waldcard. + * 0 to (~0 - 1): is one of the number of each value. + */ +struct secpolicyindex { + u_int8_t dir; /* direction of packet flow, see blow */ + struct sockaddr_storage src; /* IP src address for SP */ + struct sockaddr_storage dst; /* IP dst address for SP */ + u_int8_t prefs; /* prefix length in bits for src */ + u_int8_t prefd; /* prefix length in bits for dst */ + u_int16_t ul_proto; /* upper layer Protocol */ +}; + +/* Security Policy Data Base */ +struct secpolicy { + LIST_ENTRY(secpolicy) chain; + + int refcnt; /* reference count */ + struct secpolicyindex spidx; /* selector */ + u_int state; /* 0: dead, others: alive */ +#define IPSEC_SPSTATE_DEAD 0 +#define IPSEC_SPSTATE_ALIVE 1 + + u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */ + struct ipsecrequest *req; + /* pointer to the ipsec request tree, */ + /* if policy == IPSEC else this value == NULL.*/ +}; + +/* Request for IPsec */ +struct ipsecrequest { + struct ipsecrequest *next; + /* pointer to next structure */ + /* If NULL, it means the end of chain. */ + struct secasindex saidx; + u_int level; /* IPsec level defined below. */ + + struct secasvar *sav; /* place holder of SA for use */ + struct secpolicy *sp; /* back pointer to SP */ +}; + +/* security policy in PCB */ +struct inpcbpolicy { + struct secpolicy *sp_in; + struct secpolicy *sp_out; + int priv; /* privileged socket ? */ +}; +#endif /*KERNEL*/ + +#define IPSEC_PORT_ANY 65535 +#define IPSEC_ULPROTO_ANY 255 +#define IPSEC_PROTO_ANY 65535 + +/* mode of security protocol */ +/* NOTE: DON'T use IPSEC_MODE_ANY at SPD. It's only use in SAD */ +#define IPSEC_MODE_ANY 0 /* i.e. wildcard. */ +#define IPSEC_MODE_TRANSPORT 1 +#define IPSEC_MODE_TUNNEL 2 + +/* + * Direction of security policy. + * NOTE: Since INVALID is used just as flag. + * The other are used for loop counter too. + */ +#define IPSEC_DIR_ANY 0 +#define IPSEC_DIR_INBOUND 1 +#define IPSEC_DIR_OUTBOUND 2 +#define IPSEC_DIR_MAX 3 +#define IPSEC_DIR_INVALID 4 + +/* Policy level */ +/* + * IPSEC, ENTRUST and BYPASS are allowd for setsockopt() in PCB, + * DISCARD, IPSEC and NONE are allowd for setkey() in SPD. + * DISCARD and NONE are allowd for system default. + */ +#define IPSEC_POLICY_DISCARD 0 /* discarding packet */ +#define IPSEC_POLICY_NONE 1 /* through IPsec engine */ +#define IPSEC_POLICY_IPSEC 2 /* do IPsec */ +#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */ +#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */ + +/* Security protocol level */ +#define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */ +#define IPSEC_LEVEL_USE 1 /* use SA if present. */ +#define IPSEC_LEVEL_REQUIRE 2 /* require SA. */ +#define IPSEC_LEVEL_UNIQUE 3 /* unique SA. */ + +#define IPSEC_REPLAYWSIZE 32 + +/* statistics for ipsec processing */ +struct ipsecstat { + u_long in_success; /* succeeded inbound process */ + u_long in_polvio; /* security policy violation for inbound process */ + u_long in_nosa; /* inbound SA is unavailable */ + u_long in_inval; /* inbound processing failed due to EINVAL */ + u_long in_badspi; /* failed getting a SPI */ + u_long in_ahreplay; /* AH replay check failed */ + u_long in_espreplay; /* ESP replay check failed */ + u_long in_ahauthsucc; /* AH authentication success */ + u_long in_ahauthfail; /* AH authentication failure */ + u_long in_espauthsucc; /* ESP authentication success */ + u_long in_espauthfail; /* ESP authentication failure */ + u_long in_esphist[SADB_EALG_MAX]; + u_long in_ahhist[SADB_AALG_MAX]; + u_long out_success; /* succeeded outbound process */ + u_long out_polvio; /* security policy violation for outbound process */ + u_long out_nosa; /* outbound SA is unavailable */ + u_long out_inval; /* outbound process failed due to EINVAL */ + u_long out_noroute; /* there is no route */ + u_long out_esphist[SADB_EALG_MAX]; + u_long out_ahhist[SADB_AALG_MAX]; +}; + +/* + * Definitions for IPsec & Key sysctl operations. + */ +/* + * Names for IPsec & Key sysctl objects + */ +#define IPSECCTL_STATS 1 /* stats */ +#define IPSECCTL_DEF_POLICY 2 +#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */ +#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */ +#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */ +#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */ +#define IPSECCTL_INBOUND_CALL_IKE 7 +#define IPSECCTL_AH_CLEARTOS 8 +#define IPSECCTL_AH_OFFSETMASK 9 +#define IPSECCTL_DFBIT 10 +#define IPSECCTL_ECN 11 +#define IPSECCTL_MAXID 12 + +#define IPSECCTL_NAMES { \ + { 0, 0 }, \ + { 0, 0 }, \ + { "def_policy", CTLTYPE_INT }, \ + { "esp_trans_deflev", CTLTYPE_INT }, \ + { "esp_net_deflev", CTLTYPE_INT }, \ + { "ah_trans_deflev", CTLTYPE_INT }, \ + { "ah_net_deflev", CTLTYPE_INT }, \ + { "inbound_call_ike", CTLTYPE_INT }, \ + { "ah_cleartos", CTLTYPE_INT }, \ + { "ah_offsetmask", CTLTYPE_INT }, \ + { "dfbit", CTLTYPE_INT }, \ + { "ecn", CTLTYPE_INT }, \ +} + +#define IPSEC6CTL_NAMES { \ + { 0, 0 }, \ + { 0, 0 }, \ + { "def_policy", CTLTYPE_INT }, \ + { "esp_trans_deflev", CTLTYPE_INT }, \ + { "esp_net_deflev", CTLTYPE_INT }, \ + { "ah_trans_deflev", CTLTYPE_INT }, \ + { "ah_net_deflev", CTLTYPE_INT }, \ + { "inbound_call_ike", CTLTYPE_INT }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { "ecn", CTLTYPE_INT }, \ +} + +#define IPSECCTL_VARS { \ + 0, \ + 0, \ + &ip4_def_policy.policy, \ + &ip4_esp_trans_deflev, \ + &ip4_esp_net_deflev, \ + &ip4_ah_trans_deflev, \ + &ip4_ah_net_deflev, \ + &ip4_inbound_call_ike, \ + &ip4_ah_cleartos, \ + &ip4_ah_offsetmask, \ + &ip4_ipsec_dfbit, \ + &ip4_ipsec_ecn, \ +} + +#define IPSEC6CTL_VARS { \ + 0, \ + 0, \ + &ip6_def_policy.policy, \ + &ip6_esp_trans_deflev, \ + &ip6_esp_net_deflev, \ + &ip6_ah_trans_deflev, \ + &ip6_ah_net_deflev, \ + &ip6_inbound_call_ike, \ + 0, \ + 0, \ + 0, \ + &ip6_ipsec_ecn, \ +} + +#ifdef KERNEL +struct ipsec_output_state { + struct mbuf *m; + struct route *ro; + struct sockaddr *dst; +}; + +extern struct ipsecstat ipsecstat; +extern struct secpolicy ip4_def_policy; +extern int ip4_esp_trans_deflev; +extern int ip4_esp_net_deflev; +extern int ip4_ah_trans_deflev; +extern int ip4_ah_net_deflev; +extern int ip4_inbound_call_ike; +extern int ip4_ah_cleartos; +extern int ip4_ah_offsetmask; +extern int ip4_ipsec_dfbit; +extern int ip4_ipsec_ecn; + +extern struct secpolicy *ipsec4_getpolicybysock + __P((struct mbuf *, u_int, struct socket *, int *)); +extern struct secpolicy *ipsec4_getpolicybyaddr + __P((struct mbuf *, u_int, int, int *)); + +struct inpcb; + +extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **)); +extern int ipsec_copy_policy + __P((struct inpcbpolicy *, struct inpcbpolicy *)); +extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *)); + +extern int ipsec4_set_policy __P((struct inpcb *inp, int optname, + caddr_t request, int priv)); +extern int ipsec4_get_policy + __P((struct inpcb *inpcb, caddr_t request, struct mbuf **mp)); +extern int ipsec4_delete_pcbpolicy __P((struct inpcb *)); +extern int ipsec4_in_reject_so __P((struct mbuf *, struct socket *)); +extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *)); + +struct secas; +struct tcpcb; +struct tcp6cb; +extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *)); +extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *)); + +extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); +extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *, int)); + +struct ip; + +extern const char *ipsec4_logpacketstr __P((struct ip *, u_int32_t)); +extern const char *ipsec_logsastr __P((struct secasvar *)); + +extern void ipsec_dumpmbuf __P((struct mbuf *)); + +extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *, + int)); + +extern int ipsec4_tunnel_validate __P((struct ip *, u_int, + struct secasvar *)); + +extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); + +#endif /*KERNEL*/ + +#ifndef KERNEL +extern caddr_t ipsec_set_policy __P((char *policy, int buflen)); +extern int ipsec_get_policylen __P((caddr_t buf)); +extern char *ipsec_dump_policy __P((caddr_t buf, char *delimiter)); + +extern char *ipsec_strerror __P((void)); +#endif /*!KERNEL*/ + +#endif /*_NETINET6_IPSEC_H_*/ + diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h new file mode 100644 index 0000000..61896b3 --- /dev/null +++ b/sys/netinet6/ipsec6.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * IPsec controller part, only IPv6 related + */ + +#ifndef _NETINET6_IPSEC6_H_ +#define _NETINET6_IPSEC6_H_ + +#ifdef KERNEL + +extern struct ipsecstat ipsec6stat; +extern struct secpolicy ip6_def_policy; +extern int ip6_esp_trans_deflev; +extern int ip6_esp_net_deflev; +extern int ip6_ah_trans_deflev; +extern int ip6_ah_net_deflev; +extern int ip6_inbound_call_ike; +extern int ip6_ipsec_ecn; + +extern struct secpolicy *ipsec6_getpolicybysock + __P((struct mbuf *, u_int, struct socket *, int *)); +extern struct secpolicy *ipsec6_getpolicybyaddr + __P((struct mbuf *, u_int, int, int *)); + +extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *)); +extern int ipsec6_delete_pcbpolicy __P((struct inpcb *)); +extern int ipsec6_set_policy __P((struct inpcb *inp, int optname, + caddr_t request, int priv)); +extern int ipsec6_get_policy + __P((struct inpcb *inp, caddr_t request, struct mbuf **mp)); +extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *)); + +extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); + +struct ip6_hdr; + +extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t)); +extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *, + struct mbuf *, struct secpolicy *, + int, int *)); +extern int ipsec6_output_tunnel __P((struct ipsec_output_state *, + struct secpolicy *, int)); +extern int ipsec6_tunnel_validate __P((struct ip6_hdr *, u_int, + struct secasvar *)); + +#endif /*KERNEL*/ +#endif /* _NETINET6_IPSEC6_H_ */ diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c new file mode 100644 index 0000000..8c5824a --- /dev/null +++ b/sys/netinet6/mld6.c @@ -0,0 +1,459 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1988 Stephen Deering. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)igmp.c 8.1 (Berkeley) 7/19/93 + */ + +#include "opt_inet.h" +#include "opt_ipsec.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Protocol constants + */ + +/* denotes that the MLD max response delay field specifies time in milliseconds */ +#define MLD6_TIMER_SCALE 1000 +/* + * time between repetitions of a node's initial report of interest in a + * multicast address(in seconds) + */ +#define MLD6_UNSOLICITED_REPORT_INTERVAL 10 + +static struct ip6_pktopts ip6_opts; +static int mld6_timers_are_running; +/* XXX: These are necessary for KAME's link-local hack */ +static struct in6_addr mld6_all_nodes_linklocal = + IN6ADDR_LINKLOCAL_ALLNODES_INIT; +static struct in6_addr mld6_all_routers_linklocal = + IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; + +static void mld6_sendpkt __P((struct in6_multi *, int, + const struct in6_addr *)); + +void +mld6_init() +{ + static u_int8_t hbh_buf[8]; + struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf; + u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD); + + mld6_timers_are_running = 0; + + /* ip6h_nxt will be fill in later */ + hbh->ip6h_len = 0; /* (8 >> 3) - 1*/ + + /* XXX: grotty hard coding... */ + hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */ + hbh_buf[3] = 0; + hbh_buf[4] = IP6OPT_RTALERT; + hbh_buf[5] = IP6OPT_RTALERT_LEN - 2; + bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t)); + + ip6_opts.ip6po_hbh = hbh; + /* We will specify the hoplimit by a multicast option. */ + ip6_opts.ip6po_hlim = -1; +} + +void +mld6_start_listening(in6m) + struct in6_multi *in6m; +{ + int s = splnet(); + + /* + * (draft-ietf-ipngwg-mld, page 10) + * The node never sends a Report or Done for the link-scope all-nodes + * address. + * MLD messages are never sent for multicast addresses whose scope is 0 + * (reserved) or 1 (node-local). + */ + mld6_all_nodes_linklocal.s6_addr16[1] = + htons(in6m->in6m_ifp->if_index); /* XXX */ + if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) || + IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) { + in6m->in6m_timer = 0; + in6m->in6m_state = MLD6_OTHERLISTENER; + } else { + mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); + in6m->in6m_timer = MLD6_RANDOM_DELAY( + MLD6_UNSOLICITED_REPORT_INTERVAL * PR_FASTHZ); + in6m->in6m_state = MLD6_IREPORTEDLAST; + mld6_timers_are_running = 1; + } + splx(s); +} + +void +mld6_stop_listening(in6m) + struct in6_multi *in6m; +{ + mld6_all_nodes_linklocal.s6_addr16[1] = + htons(in6m->in6m_ifp->if_index); /* XXX */ + mld6_all_routers_linklocal.s6_addr16[1] = + htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */ + + if (in6m->in6m_state == MLD6_IREPORTEDLAST && + (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) && + IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL) + mld6_sendpkt(in6m, MLD6_LISTENER_DONE, + &mld6_all_routers_linklocal); +} + +void +mld6_input(m, off) + struct mbuf *m; + int off; +{ + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct in6_multi *in6m; + struct in6_ifaddr *ia; + struct ifmultiaddr *ifma; + int timer; /* timer value in the MLD query header */ + + /* source address validation */ + if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { + log(LOG_ERR, + "mld6_input: src %s is not link-local\n", + ip6_sprintf(&ip6->ip6_src)); + /* + * spec(draft-ietf-ipngwg-mld) does not explicitly + * specify to discard the packet from a non link-local + * source address. But we believe it's expected to do so. + */ + return; + } + + /* + * In the MLD6 specification, there are 3 states and a flag. + * + * In Non-Listener state, we simply don't have a membership record. + * In Delaying Listener state, our timer is running (in6m->in6m_timer) + * In Idle Listener state, our timer is not running (in6m->in6m_timer==0) + * + * The flag is in6m->in6m_state, it is set to MLD6_OTHERLISTENER if + * we have heard a report from another member, or MLD6_IREPORTEDLAST + * if we sent the last report. + */ + switch(mldh->mld6_type) { + case MLD6_LISTENER_QUERY: + if (ifp->if_flags & IFF_LOOPBACK) + break; + + if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) && + !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) + break; /* print error or log stat? */ + if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) + mldh->mld6_addr.s6_addr16[1] = + htons(ifp->if_index); /* XXX */ + + /* + * - Start the timers in all of our membership records + * that the query applies to for the interface on + * which the query arrived excl. those that belong + * to the "all-nodes" group (ff02::1). + * - Restart any timer that is already running but has + * A value longer than the requested timeout. + * - Use the value specified in the query message as + * the maximum timeout. + */ + IFP_TO_IA6(ifp, ia); + if (ia == NULL) + break; + + /* + * XXX: System timer resolution is too low to handle Max + * Response Delay, so set 1 to the internal timer even if + * the calculated value equals to zero when Max Response + * Delay is positive. + */ + timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE; + if (timer == 0 && mldh->mld6_maxdelay) + timer = 1; + mld6_all_nodes_linklocal.s6_addr16[1] = + htons(ifp->if_index); /* XXX */ + + LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_INET6) + continue; + in6m = (struct in6_multi *)ifma->ifma_protospec; + if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, + &mld6_all_nodes_linklocal) || + IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < + IPV6_ADDR_SCOPE_LINKLOCAL) + continue; + + if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) || + IN6_ARE_ADDR_EQUAL(&mldh->mld6_addr, + &in6m->in6m_addr)) + { + if (timer == 0) { + /* send a report immediately */ + mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, + NULL); + in6m->in6m_timer = 0; /* reset timer */ + in6m->in6m_state = MLD6_IREPORTEDLAST; + } else if (in6m->in6m_timer == 0 || /*idle state*/ + in6m->in6m_timer > timer) { + in6m->in6m_timer = + MLD6_RANDOM_DELAY(timer); + mld6_timers_are_running = 1; + } + } + } + + if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) + mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ + break; + case MLD6_LISTENER_REPORT: + /* + * For fast leave to work, we have to know that we are the + * last person to send a report for this group. Reports + * can potentially get looped back if we are a multicast + * router, so discard reports sourced by me. + * Note that it is impossible to check IFF_LOOPBACK flag of + * ifp for this purpose, since ip6_mloopback pass the physical + * interface to looutput. + */ + if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */ + break; + + if (!IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) + break; + + if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) + mldh->mld6_addr.s6_addr16[1] = + htons(ifp->if_index); /* XXX */ + /* + * If we belong to the group being reported, stop + * our timer for that group. + */ + IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m); + if (in6m) { + in6m->in6m_timer = 0; /* transit to idle state */ + in6m->in6m_state = MLD6_OTHERLISTENER; /* clear flag */ + } + + if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) + mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ + break; + default: /* this is impossible */ + log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type); + break; + } +} + +void +mld6_fasttimeo() +{ + register struct in6_multi *in6m; + struct in6_multistep step; + int s; + + /* + * Quick check to see if any work needs to be done, in order + * to minimize the overhead of fasttimo processing. + */ + if (!mld6_timers_are_running) + return; + + s = splnet(); + mld6_timers_are_running = 0; + IN6_FIRST_MULTI(step, in6m); + while (in6m != NULL) { + if (in6m->in6m_timer == 0) { + /* do nothing */ + } else if (--in6m->in6m_timer == 0) { + mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); + in6m->in6m_state = MLD6_IREPORTEDLAST; + } else { + mld6_timers_are_running = 1; + } + IN6_NEXT_MULTI(step, in6m); + } + splx(s); +} + +static void +mld6_sendpkt(in6m, type, dst) + struct in6_multi *in6m; + int type; + const struct in6_addr *dst; +{ + struct mbuf *mh, *md; + struct mld6_hdr *mldh; + struct ip6_hdr *ip6; + struct ip6_moptions im6o; + struct in6_ifaddr *ia; + struct ifnet *ifp = in6m->in6m_ifp; + struct ifnet *outif = NULL; + + /* + * At first, find a link local address on the outgoing interface + * to use as the source address of the MLD packet. + */ + if ((ia = in6ifa_ifpforlinklocal(ifp)) == NULL) + return; + + /* + * Allocate mbufs to store ip6 header and MLD header. + * We allocate 2 mbufs and make chain in advance because + * it is more convenient when inserting the hop-by-hop option later. + */ + MGETHDR(mh, M_DONTWAIT, MT_HEADER); + if (mh == NULL) + return; + MGET(md, M_DONTWAIT, MT_DATA); + if (md == NULL) { + m_free(mh); + return; + } + mh->m_next = md; + +#ifdef IPSEC + mh->m_pkthdr.rcvif = NULL; +#endif + mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); + mh->m_len = sizeof(struct ip6_hdr); + MH_ALIGN(mh, sizeof(struct ip6_hdr)); + + /* fill in the ip6 header */ + ip6 = mtod(mh, struct ip6_hdr *); + ip6->ip6_flow = 0; + ip6->ip6_vfc = IPV6_VERSION; + /* ip6_plen will be set later */ + ip6->ip6_nxt = IPPROTO_ICMPV6; + /* ip6_hlim will be set by im6o.im6o_multicast_hlim */ + ip6->ip6_src = ia->ia_addr.sin6_addr; + ip6->ip6_dst = dst ? *dst : in6m->in6m_addr; + + /* fill in the MLD header */ + md->m_len = sizeof(struct mld6_hdr); + mldh = mtod(md, struct mld6_hdr *); + mldh->mld6_type = type; + mldh->mld6_code = 0; + mldh->mld6_cksum = 0; + /* XXX: we assume the function will not be called for query messages */ + mldh->mld6_maxdelay = 0; + mldh->mld6_reserved = 0; + mldh->mld6_addr = in6m->in6m_addr; + if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) + mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ + mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), + sizeof(struct mld6_hdr)); + + /* construct multicast option */ + bzero(&im6o, sizeof(im6o)); + im6o.im6o_multicast_ifp = ifp; + im6o.im6o_multicast_hlim = 1; + + /* + * Request loopback of the report if we are acting as a multicast + * router, so that the process-level routing daemon can hear it. + */ + im6o.im6o_multicast_loop = 0; + + /* increment output statictics */ + icmp6stat.icp6s_outhist[type]++; + + ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif); + if (outif) { + icmp6_ifstat_inc(outif, ifs6_out_msg); + switch(type) { + case MLD6_LISTENER_QUERY: + icmp6_ifstat_inc(outif, ifs6_out_mldquery); + break; + case MLD6_LISTENER_REPORT: + icmp6_ifstat_inc(outif, ifs6_out_mldreport); + break; + case MLD6_LISTENER_DONE: + icmp6_ifstat_inc(outif, ifs6_out_mlddone); + break; + } + } +} diff --git a/sys/netinet6/mld6_var.h b/sys/netinet6/mld6_var.h new file mode 100644 index 0000000..ab50c59 --- /dev/null +++ b/sys/netinet6/mld6_var.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETINET6_MLD6_VAR_H_ +#define _NETINET6_MLD6_VAR_H_ + +#ifdef _KERNEL + +#define MLD6_RANDOM_DELAY(X) (random() % (X) + 1) + +/* + * States for MLD stop-listening processing + */ +#define MLD6_OTHERLISTENER 0 +#define MLD6_IREPORTEDLAST 1 + +void mld6_init __P((void)); +void mld6_input __P((struct mbuf *, int)); +void mld6_start_listening __P((struct in6_multi *)); +void mld6_stop_listening __P((struct in6_multi *)); +void mld6_fasttimeo __P((void)); +#endif /* _KERNEL */ + +#endif /* _NETINET6_MLD6_VAR_H_ */ diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c new file mode 100644 index 0000000..87eab9d --- /dev/null +++ b/sys/netinet6/nd6.c @@ -0,0 +1,1531 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * XXX + * KAME 970409 note: + * BSD/OS version heavily modifies this code, related to llinfo. + * Since we don't have BSD/OS version of net/route.c in our hand, + * I left the code mostly as it was in 970310. -- itojun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "loop.h" + +#include + +#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ +#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ + +#define SIN6(s) ((struct sockaddr_in6 *)s) +#define SDL(s) ((struct sockaddr_dl *)s) + +/* timer values */ +int nd6_prune = 1; /* walk list every 1 seconds */ +int nd6_delay = 5; /* delay first probe time 5 second */ +int nd6_umaxtries = 3; /* maximum unicast query */ +int nd6_mmaxtries = 3; /* maximum multicast query */ +int nd6_useloopback = 1; /* use loopback interface for local traffic */ +int nd6_proxyall = 0; /* enable Proxy Neighbor Advertisement */ + +/* for debugging? */ +static int nd6_inuse, nd6_allocated; + +struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6}; +struct nd_ifinfo *nd_ifinfo = NULL; +struct nd_drhead nd_defrouter = { 0 }; +struct nd_prhead nd_prefix = { 0 }; + +int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; +static struct sockaddr_in6 all1_sa; + +static void nd6_slowtimo __P((void *)); + +void +nd6_init() +{ + static int nd6_init_done = 0; + int i; + + if (nd6_init_done) { + log(LOG_NOTICE, "nd6_init called more than once(ignored)\n"); + return; + } + + all1_sa.sin6_family = AF_INET6; + all1_sa.sin6_len = sizeof(struct sockaddr_in6); + for (i = 0; i < sizeof(all1_sa.sin6_addr); i++) + all1_sa.sin6_addr.s6_addr[i] = 0xff; + + nd6_init_done = 1; + + /* start timer */ + timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); +} + +void +nd6_ifattach(ifp) + struct ifnet *ifp; +{ + static size_t if_indexlim = 8; + + /* + * We have some arrays that should be indexed by if_index. + * since if_index will grow dynamically, they should grow too. + */ + if (nd_ifinfo == NULL || if_index >= if_indexlim) { + size_t n; + caddr_t q; + + while (if_index >= if_indexlim) + if_indexlim <<= 1; + + /* grow nd_ifinfo */ + n = if_indexlim * sizeof(struct nd_ifinfo); + q = (caddr_t)malloc(n, M_IP6NDP, M_WAITOK); + bzero(q, n); + if (nd_ifinfo) { + bcopy((caddr_t)nd_ifinfo, q, n/2); + free((caddr_t)nd_ifinfo, M_IP6NDP); + } + nd_ifinfo = (struct nd_ifinfo *)q; + } + +#define ND nd_ifinfo[ifp->if_index] + ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu; + ND.chlim = IPV6_DEFHLIM; + ND.basereachable = REACHABLE_TIME; + ND.reachable = ND_COMPUTE_RTIME(ND.basereachable); + ND.retrans = RETRANS_TIMER; + ND.receivedra = 0; + nd6_setmtu(ifp); +#undef ND +} + +/* + * Reset ND level link MTU. This function is called when the physical MTU + * changes, which means we might have to adjust the ND level MTU. + */ +void +nd6_setmtu(ifp) + struct ifnet *ifp; +{ +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; + u_long oldmaxmtu = ndi->maxmtu; + u_long oldlinkmtu = ndi->linkmtu; + + switch(ifp->if_type) { + case IFT_ARCNET: /* XXX MTU handling needs more work */ + ndi->maxmtu = MIN(60480, ifp->if_mtu); + break; + case IFT_ETHER: + ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); + break; + case IFT_FDDI: + ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); + break; + case IFT_ATM: + ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu); + break; + default: + ndi->maxmtu = ifp->if_mtu; + break; + } + + if (oldmaxmtu != ndi->maxmtu) { + /* + * If the ND level MTU is not set yet, or if the maxmtu + * is reset to a smaller value than the ND level MTU, + * also reset the ND level MTU. + */ + if (ndi->linkmtu == 0 || + ndi->maxmtu < ndi->linkmtu) { + ndi->linkmtu = ndi->maxmtu; + /* also adjust in6_maxmtu if necessary. */ + if (oldlinkmtu == 0) { + /* + * XXX: the case analysis is grotty, but + * it is not efficient to call in6_setmaxmtu() + * here when we are during the initialization + * procedure. + */ + if (in6_maxmtu < ndi->linkmtu) + in6_maxmtu = ndi->linkmtu; + } else + in6_setmaxmtu(); + } + } +#undef MIN +} + +void +nd6_option_init(opt, icmp6len, ndopts) + void *opt; + int icmp6len; + union nd_opts *ndopts; +{ + bzero(ndopts, sizeof(*ndopts)); + ndopts->nd_opts_search = (struct nd_opt_hdr *)opt; + ndopts->nd_opts_last + = (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len); + + if (icmp6len == 0) { + ndopts->nd_opts_done = 1; + ndopts->nd_opts_search = NULL; + } +} + +/* + * Take one ND option. + */ +struct nd_opt_hdr * +nd6_option(ndopts) + union nd_opts *ndopts; +{ + struct nd_opt_hdr *nd_opt; + int olen; + + if (!ndopts) + panic("ndopts == NULL in nd6_option\n"); + if (!ndopts->nd_opts_last) + panic("uninitialized ndopts in nd6_option\n"); + if (!ndopts->nd_opts_search) + return NULL; + if (ndopts->nd_opts_done) + return NULL; + + nd_opt = ndopts->nd_opts_search; + + olen = nd_opt->nd_opt_len << 3; + if (olen == 0) { + /* + * Message validation requires that all included + * options have a length that is greater than zero. + */ + bzero(ndopts, sizeof(*ndopts)); + return NULL; + } + + ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen); + if (!(ndopts->nd_opts_search < ndopts->nd_opts_last)) { + ndopts->nd_opts_done = 1; + ndopts->nd_opts_search = NULL; + } + return nd_opt; +} + +/* + * Parse multiple ND options. + * This function is much easier to use, for ND routines that do not need + * multiple options of the same type. + */ +int +nd6_options(ndopts) + union nd_opts *ndopts; +{ + struct nd_opt_hdr *nd_opt; + int i = 0; + + if (!ndopts) + panic("ndopts == NULL in nd6_options\n"); + if (!ndopts->nd_opts_last) + panic("uninitialized ndopts in nd6_options\n"); + if (!ndopts->nd_opts_search) + return 0; + + while (1) { + nd_opt = nd6_option(ndopts); + if (!nd_opt && !ndopts->nd_opts_last) { + /* + * Message validation requires that all included + * options have a length that is greater than zero. + */ + bzero(ndopts, sizeof(*ndopts)); + return -1; + } + + if (!nd_opt) + goto skip1; + + switch (nd_opt->nd_opt_type) { + case ND_OPT_SOURCE_LINKADDR: + case ND_OPT_TARGET_LINKADDR: + case ND_OPT_MTU: + case ND_OPT_REDIRECTED_HEADER: + if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { + printf("duplicated ND6 option found " + "(type=%d)\n", nd_opt->nd_opt_type); + /* XXX bark? */ + } else { + ndopts->nd_opt_array[nd_opt->nd_opt_type] + = nd_opt; + } + break; + case ND_OPT_PREFIX_INFORMATION: + if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) { + ndopts->nd_opt_array[nd_opt->nd_opt_type] + = nd_opt; + } + ndopts->nd_opts_pi_end = + (struct nd_opt_prefix_info *)nd_opt; + break; + default: + /* + * Unknown options must be silently ignored, + * to accomodate future extension to the protocol. + */ + log(LOG_INFO, + "nd6_options: unsupported option %d - " + "option ignored\n", nd_opt->nd_opt_type); + } + +skip1: + i++; + if (i > 10) { + printf("too many loop in nd opt\n"); + break; + } + + if (ndopts->nd_opts_done) + break; + } + + return 0; +} + +/* + * ND6 timer routine to expire default route list and prefix list + */ +void +nd6_timer(ignored_arg) + void *ignored_arg; +{ + int s; + register struct llinfo_nd6 *ln; + register struct nd_defrouter *dr; + register struct nd_prefix *pr; + + s = splnet(); + timeout(nd6_timer, (caddr_t)0, nd6_prune * hz); + + ln = llinfo_nd6.ln_next; + /* XXX BSD/OS separates this code -- itojun */ + while (ln && ln != &llinfo_nd6) { + struct rtentry *rt; + struct ifnet *ifp; + struct sockaddr_in6 *dst; + struct llinfo_nd6 *next = ln->ln_next; + + if ((rt = ln->ln_rt) == NULL) { + ln = next; + continue; + } + if ((ifp = rt->rt_ifp) == NULL) { + ln = next; + continue; + } + dst = (struct sockaddr_in6 *)rt_key(rt); + + if (ln->ln_expire > time_second) { + ln = next; + continue; + } + + /* sanity check */ + if (!rt) + panic("rt=0 in nd6_timer(ln=%p)\n", ln); + if (!dst) + panic("dst=0 in nd6_timer(ln=%p)\n", ln); + + switch (ln->ln_state) { + case ND6_LLINFO_INCOMPLETE: + if (ln->ln_asked < nd6_mmaxtries) { + ln->ln_asked++; + ln->ln_expire = time_second + + nd_ifinfo[ifp->if_index].retrans / 1000; + nd6_ns_output(ifp, NULL, &dst->sin6_addr, + ln, 0); + } else { + struct mbuf *m = ln->ln_hold; + if (m) { + if (rt->rt_ifp) { + /* + * Fake rcvif to make ICMP error + * more helpful in diagnosing + * for the receiver. + * XXX: should we consider + * older rcvif? + */ + m->m_pkthdr.rcvif = rt->rt_ifp; + } + icmp6_error(m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0); + ln->ln_hold = NULL; + } + nd6_free(rt); + } + break; + case ND6_LLINFO_REACHABLE: + if (ln->ln_expire) { + ln->ln_state = ND6_LLINFO_STALE; + } + break; + /* + * ND6_LLINFO_STALE state requires nothing for timer + * routine. + */ + case ND6_LLINFO_DELAY: + ln->ln_asked = 1; + ln->ln_state = ND6_LLINFO_PROBE; + ln->ln_expire = time_second + + nd_ifinfo[ifp->if_index].retrans / 1000; + nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, + ln, 0); + break; + + case ND6_LLINFO_PROBE: + if (ln->ln_asked < nd6_umaxtries) { + ln->ln_asked++; + ln->ln_expire = time_second + + nd_ifinfo[ifp->if_index].retrans / 1000; + nd6_ns_output(ifp, &dst->sin6_addr, + &dst->sin6_addr, ln, 0); + } else { + nd6_free(rt); + } + break; + case ND6_LLINFO_WAITDELETE: + nd6_free(rt); + break; + } + ln = next; + } + + /* expire */ + dr = nd_defrouter.lh_first; + while (dr) { + if (dr->expire && dr->expire < time_second) { + struct nd_defrouter *t; + t = dr->dr_next; + defrtrlist_del(dr); + dr = t; + } else + dr = dr->dr_next; + } + pr = nd_prefix.lh_first; + while (pr) { + struct in6_ifaddr *ia6; + struct in6_addrlifetime *lt6; + + if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + ia6 = NULL; + else + ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + + if (ia6) { + /* check address lifetime */ + lt6 = &ia6->ia6_lifetime; + if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second) + ia6->ia6_flags |= IN6_IFF_DEPRECATED; + if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) { + if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + /* xxx ND_OPT_PI_FLAG_ONLINK processing */ + } + } + + /* + * check prefix lifetime. + * since pltime is just for autoconf, pltime processing for + * prefix is not necessary. + * + * we offset expire time by NDPR_KEEP_EXPIRE, so that we + * can use the old prefix information to validate the + * next prefix information to come. See prelist_update() + * for actual validation. + */ + if (pr->ndpr_expire + && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) { + struct nd_prefix *t; + t = pr->ndpr_next; + + /* + * address expiration and prefix expiration are + * separate. NEVER perform in6_ifdel here. + */ + + prelist_remove(pr); + pr = t; + } else + pr = pr->ndpr_next; + } + splx(s); +} + +struct rtentry * +nd6_lookup(addr6, create, ifp) + struct in6_addr *addr6; + int create; + struct ifnet *ifp; +{ + struct rtentry *rt; + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr6; + rt = rtalloc1((struct sockaddr *)&sin6, create, 0UL); + if (rt && (rt->rt_flags & RTF_LLINFO) == 0) { + /* + * This is the case for the default route. + * If we want to create a neighbor cache for the address, we + * should free the route for the destination and allocate an + * interface route. + */ + if (create) { + RTFREE(rt); + rt = 0; + } + } + if (!rt) { + if (create && ifp) { + /* + * If no route is available and create is set, + * we allocate a host route for the destination + * and treat it like an interface route. + * This hack is necessary for a neighbor which can't + * be covered by our own prefix. + */ + struct ifaddr *ifa = + ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp); + if (ifa == NULL) + return(NULL); + + /* + * Create a new route. RTF_LLINFO is necessary + * to create a Neighbor Cache entry for the + * destination in nd6_rtrequest which will be + * called in rtequest via ifa->ifa_rtrequest. + */ + if (rtrequest(RTM_ADD, (struct sockaddr *)&sin6, + ifa->ifa_addr, + (struct sockaddr *)&all1_sa, + (ifa->ifa_flags | + RTF_HOST | RTF_LLINFO) & ~RTF_CLONING, + &rt)) + log(LOG_ERR, + "nd6_lookup: failed to add route for a " + "neighbor(%s)\n", ip6_sprintf(addr6)); + if (rt == NULL) + return(NULL); + if (rt->rt_llinfo) { + struct llinfo_nd6 *ln = + (struct llinfo_nd6 *)rt->rt_llinfo; + ln->ln_state = ND6_LLINFO_NOSTATE; + } + } else + return(NULL); + } + rt->rt_refcnt--; + /* + * Validation for the entry. + * XXX: we can't use rt->rt_ifp to check for the interface, since + * it might be the loopback interface if the entry is for our + * own address on a non-loopback interface. Instead, we should + * use rt->rt_ifa->ifa_ifp, which would specify the REAL interface. + */ + if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 || + rt->rt_gateway->sa_family != AF_LINK || + (ifp && rt->rt_ifa->ifa_ifp != ifp)) { + if (create) { + log(LOG_DEBUG, "nd6_lookup: failed to lookup %s (if = %s)\n", + ip6_sprintf(addr6), ifp ? if_name(ifp) : "unspec"); + /* xxx more logs... kazu */ + } + return(0); + } + return(rt); +} + +/* + * Detect if a given IPv6 address identifies a neighbor on a given link. + * XXX: should take care of the destination of a p2p link? + */ +int +nd6_is_addr_neighbor(addr, ifp) + struct in6_addr *addr; + struct ifnet *ifp; +{ + register struct ifaddr *ifa; + int i; + +#define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr) +#define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr) + + /* A link-local address is always a neighbor. */ + if (IN6_IS_ADDR_LINKLOCAL(addr)) + return(1); + + /* + * If the address matches one of our addresses, + * it should be a neighbor. + */ + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) + { + if (ifa->ifa_addr->sa_family != AF_INET6) + next: continue; + + for (i = 0; i < 4; i++) { + if ((IFADDR6(ifa).s6_addr32[i] ^ addr->s6_addr32[i]) & + IFMASK6(ifa).s6_addr32[i]) + goto next; + } + return(1); + } + + /* + * Even if the address matches none of our addresses, it might be + * in the neighbor cache. + */ + if (nd6_lookup(addr, 0, ifp)) + return(1); + + return(0); +#undef IFADDR6 +#undef IFMASK6 +} + +/* + * Free an nd6 llinfo entry. + */ +void +nd6_free(rt) + struct rtentry *rt; +{ + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; + struct sockaddr_dl *sdl; + + if (ln->ln_router) { + /* remove from default router list */ + struct nd_defrouter *dr; + struct in6_addr *in6; + int s; + in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + + s = splnet(); + dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))-> + sin6_addr, + rt->rt_ifp); + if (dr) + defrtrlist_del(dr); + else if (!ip6_forwarding && ip6_accept_rtadv) { + /* + * rt6_flush must be called in any case. + * see the comment in nd6_na_input(). + */ + rt6_flush(in6, rt->rt_ifp); + } + splx(s); + } + + if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) && + sdl->sdl_family == AF_LINK) { + sdl->sdl_alen = 0; + ln->ln_state = ND6_LLINFO_WAITDELETE; + ln->ln_asked = 0; + rt->rt_flags &= ~RTF_REJECT; + return; + } + rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), + 0, (struct rtentry **)0); +} + +/* + * Upper-layer reachability hint for Neighbor Unreachability Detection. + * + * XXX cost-effective metods? + */ +void +nd6_nud_hint(rt, dst6) + struct rtentry *rt; + struct in6_addr *dst6; +{ + struct llinfo_nd6 *ln; + + /* + * If the caller specified "rt", use that. Otherwise, resolve the + * routing table by supplied "dst6". + */ + if (!rt) { + if (!dst6) + return; + if (!(rt = nd6_lookup(dst6, 0, NULL))) + return; + } + + if ((rt->rt_flags & RTF_GATEWAY) + || (rt->rt_flags & RTF_LLINFO) == 0 + || !rt->rt_llinfo + || !rt->rt_gateway + || rt->rt_gateway->sa_family != AF_LINK) { + /* This is not a host route. */ + return; + } + + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + if (ln->ln_state == ND6_LLINFO_INCOMPLETE) + return; + + ln->ln_state = ND6_LLINFO_REACHABLE; + if (ln->ln_expire) + ln->ln_expire = time_second + + nd_ifinfo[rt->rt_ifp->if_index].reachable; +} + +void +nd6_rtrequest(req, rt, sa) + int req; + struct rtentry *rt; + struct sockaddr *sa; /* xxx unused */ +{ + struct sockaddr *gate = rt->rt_gateway; + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; + static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; + struct ifnet *ifp = rt->rt_ifp; + struct ifaddr *ifa; + + if (rt->rt_flags & RTF_GATEWAY) + return; + + switch (req) { + case RTM_ADD: + /* + * There is no backward compatibility :) + * + * if ((rt->rt_flags & RTF_HOST) == 0 && + * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) + * rt->rt_flags |= RTF_CLONING; + */ + if (rt->rt_flags & RTF_CLONING || rt->rt_flags & RTF_LLINFO) { + /* + * Case 1: This route should come from + * a route to interface. RTF_LLINFO flag is set + * for a host route whose destination should be + * treated as on-link. + */ + rt_setgate(rt, rt_key(rt), + (struct sockaddr *)&null_sdl); + gate = rt->rt_gateway; + SDL(gate)->sdl_type = ifp->if_type; + SDL(gate)->sdl_index = ifp->if_index; + if (ln) + ln->ln_expire = time_second; + if (ln && ln->ln_expire == 0) { + /* cludge for desktops */ + ln->ln_expire = 1; + } + if (rt->rt_flags & RTF_CLONING) + break; + } + /* Announce a new entry if requested. */ + if (rt->rt_flags & RTF_ANNOUNCE) + nd6_na_output(ifp, + &SIN6(rt_key(rt))->sin6_addr, + &SIN6(rt_key(rt))->sin6_addr, + ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, + 1); + /* FALLTHROUGH */ + case RTM_RESOLVE: + if (gate->sa_family != AF_LINK || + gate->sa_len < sizeof(null_sdl)) { + log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n"); + break; + } + SDL(gate)->sdl_type = ifp->if_type; + SDL(gate)->sdl_index = ifp->if_index; + if (ln != 0) + break; /* This happens on a route change */ + /* + * Case 2: This route may come from cloning, or a manual route + * add with a LL address. + */ + R_Malloc(ln, struct llinfo_nd6 *, sizeof(*ln)); + rt->rt_llinfo = (caddr_t)ln; + if (!ln) { + log(LOG_DEBUG, "nd6_rtrequest: malloc failed\n"); + break; + } + nd6_inuse++; + nd6_allocated++; + Bzero(ln, sizeof(*ln)); + ln->ln_rt = rt; + /* this is required for "ndp" command. - shin */ + if (req == RTM_ADD) { + /* + * gate should have some valid AF_LINK entry, + * and ln->ln_expire should have some lifetime + * which is specified by ndp command. + */ + ln->ln_state = ND6_LLINFO_REACHABLE; + } else { + /* + * When req == RTM_RESOLVE, rt is created and + * initialized in rtrequest(), so rt_expire is 0. + */ + ln->ln_state = ND6_LLINFO_INCOMPLETE; + ln->ln_expire = time_second; + } + rt->rt_flags |= RTF_LLINFO; + ln->ln_next = llinfo_nd6.ln_next; + llinfo_nd6.ln_next = ln; + ln->ln_prev = &llinfo_nd6; + ln->ln_next->ln_prev = ln; + + /* + * check if rt_key(rt) is one of my address assigned + * to the interface. + */ + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, + &SIN6(rt_key(rt))->sin6_addr); + if (ifa) { + caddr_t macp = nd6_ifptomac(ifp); + ln->ln_expire = 0; + ln->ln_state = ND6_LLINFO_REACHABLE; + if (macp) { + Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen); + SDL(gate)->sdl_alen = ifp->if_addrlen; + } + if (nd6_useloopback) { + rt->rt_ifp = &loif[0]; /*XXX*/ + /* + * Make sure rt_ifa be equal to the ifaddr + * corresponding to the address. + * We need this because when we refer + * rt_ifa->ia6_flags in ip6_input, we assume + * that the rt_ifa points to the address instead + * of the loopback address. + */ + if (ifa != rt->rt_ifa) { + rt->rt_ifa->ifa_refcnt--; + ifa->ifa_refcnt++; + rt->rt_ifa = ifa; + } + } + } + break; + + case RTM_DELETE: + if (!ln) + break; + nd6_inuse--; + ln->ln_next->ln_prev = ln->ln_prev; + ln->ln_prev->ln_next = ln->ln_next; + ln->ln_prev = NULL; + rt->rt_llinfo = 0; + rt->rt_flags &= ~RTF_LLINFO; + if (ln->ln_hold) + m_freem(ln->ln_hold); + Free((caddr_t)ln); + } +} + +void +nd6_p2p_rtrequest(req, rt, sa) + int req; + struct rtentry *rt; + struct sockaddr *sa; /* xxx unused */ +{ + struct sockaddr *gate = rt->rt_gateway; + static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; + struct ifnet *ifp = rt->rt_ifp; + struct ifaddr *ifa; + + if (rt->rt_flags & RTF_GATEWAY) + return; + + switch (req) { + case RTM_ADD: + /* + * There is no backward compatibility :) + * + * if ((rt->rt_flags & RTF_HOST) == 0 && + * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) + * rt->rt_flags |= RTF_CLONING; + */ + if (rt->rt_flags & RTF_CLONING) { + /* + * Case 1: This route should come from + * a route to interface. + */ + rt_setgate(rt, rt_key(rt), + (struct sockaddr *)&null_sdl); + gate = rt->rt_gateway; + SDL(gate)->sdl_type = ifp->if_type; + SDL(gate)->sdl_index = ifp->if_index; + break; + } + /* Announce a new entry if requested. */ + if (rt->rt_flags & RTF_ANNOUNCE) + nd6_na_output(ifp, + &SIN6(rt_key(rt))->sin6_addr, + &SIN6(rt_key(rt))->sin6_addr, + ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, + 1); + /* FALLTHROUGH */ + case RTM_RESOLVE: + /* + * check if rt_key(rt) is one of my address assigned + * to the interface. + */ + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, + &SIN6(rt_key(rt))->sin6_addr); + if (ifa) { + if (nd6_useloopback) { + rt->rt_ifp = &loif[0]; /*XXX*/ + } + } + break; + } +} + +int +nd6_ioctl(cmd, data, ifp) + u_long cmd; + caddr_t data; + struct ifnet *ifp; +{ + struct in6_drlist *drl = (struct in6_drlist *)data; + struct in6_prlist *prl = (struct in6_prlist *)data; + struct in6_ndireq *ndi = (struct in6_ndireq *)data; + struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; + struct nd_defrouter *dr, any; + struct nd_prefix *pr; + struct rtentry *rt; + int i = 0, error = 0; + int s; + + switch (cmd) { + case SIOCGDRLST_IN6: + bzero(drl, sizeof(*drl)); + s = splnet(); + dr = nd_defrouter.lh_first; + while (dr && i < DRLSTSIZ) { + drl->defrouter[i].rtaddr = dr->rtaddr; + if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) { + /* XXX: need to this hack for KAME stack */ + drl->defrouter[i].rtaddr.s6_addr16[1] = 0; + } else + log(LOG_ERR, + "default router list contains a " + "non-linklocal address(%s)\n", + ip6_sprintf(&drl->defrouter[i].rtaddr)); + + drl->defrouter[i].flags = dr->flags; + drl->defrouter[i].rtlifetime = dr->rtlifetime; + drl->defrouter[i].expire = dr->expire; + drl->defrouter[i].if_index = dr->ifp->if_index; + i++; + dr = dr->dr_next; + } + splx(s); + break; + case SIOCGPRLST_IN6: + bzero(prl, sizeof(*prl)); + s = splnet(); + pr = nd_prefix.lh_first; + while (pr && i < PRLSTSIZ) { + struct nd_pfxrouter *pfr; + int j; + + prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr; + prl->prefix[i].raflags = pr->ndpr_raf; + prl->prefix[i].prefixlen = pr->ndpr_plen; + prl->prefix[i].vltime = pr->ndpr_vltime; + prl->prefix[i].pltime = pr->ndpr_pltime; + prl->prefix[i].if_index = pr->ndpr_ifp->if_index; + prl->prefix[i].expire = pr->ndpr_expire; + + pfr = pr->ndpr_advrtrs.lh_first; + j = 0; + while(pfr) { + if (j < DRLSTSIZ) { +#define RTRADDR prl->prefix[i].advrtr[j] + RTRADDR = pfr->router->rtaddr; + if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) { + /* XXX: hack for KAME */ + RTRADDR.s6_addr16[1] = 0; + } else + log(LOG_ERR, + "a router(%s) advertises " + "a prefix with " + "non-link local address\n", + ip6_sprintf(&RTRADDR)); +#undef RTRADDR + } + j++; + pfr = pfr->pfr_next; + } + prl->prefix[i].advrtrs = j; + + i++; + pr = pr->ndpr_next; + } + splx(s); + { + struct rr_prefix *rpp; + + for (rpp = LIST_FIRST(&rr_prefix); rpp; + rpp = LIST_NEXT(rpp, rp_entry)) { + if (i >= PRLSTSIZ) + break; + prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr; + prl->prefix[i].raflags = rpp->rp_raf; + prl->prefix[i].prefixlen = rpp->rp_plen; + prl->prefix[i].vltime = rpp->rp_vltime; + prl->prefix[i].pltime = rpp->rp_pltime; + prl->prefix[i].if_index = rpp->rp_ifp->if_index; + prl->prefix[i].expire = rpp->rp_expire; + prl->prefix[i].advrtrs = 0; + i++; + } + } + + break; + case SIOCGIFINFO_IN6: + ndi->ndi = nd_ifinfo[ifp->if_index]; + break; + case SIOCSNDFLUSH_IN6: + /* flush default router list */ + /* + * xxx sumikawa: should not delete route if default + * route equals to the top of default router list + */ + bzero(&any, sizeof(any)); + defrouter_delreq(&any, 0); + /* xxx sumikawa: flush prefix list */ + break; + case SIOCSPFXFLUSH_IN6: + { + /* flush all the prefix advertised by routers */ + struct nd_prefix *pr, *next; + + s = splnet(); + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; + if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + prelist_remove(pr); + } + splx(s); + break; + } + case SIOCSRTRFLUSH_IN6: + { + /* flush all the default routers */ + struct nd_defrouter *dr, *next; + + s = splnet(); + if ((dr = nd_defrouter.lh_first) != NULL) { + /* + * The first entry of the list may be stored in + * the routing table, so we'll delete it later. + */ + for (dr = dr->dr_next; dr; dr = next) { + next = dr->dr_next; + defrtrlist_del(dr); + } + defrtrlist_del(nd_defrouter.lh_first); + } + splx(s); + break; + } + case SIOCGNBRINFO_IN6: + { + struct llinfo_nd6 *ln; + struct in6_addr nb_addr = nbi->addr; /* make local for safety */ + + /* + * XXX: KAME specific hack for scoped addresses + * XXXX: for other scopes than link-local? + */ + if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) { + u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2]; + + if (*idp == 0) + *idp = htons(ifp->if_index); + } + + s = splnet(); + if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) { + error = EINVAL; + break; + } + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + nbi->state = ln->ln_state; + nbi->asked = ln->ln_asked; + nbi->isrouter = ln->ln_router; + nbi->expire = ln->ln_expire; + splx(s); + + break; + } + } + return(error); +} + +/* + * Create neighbor cache entry and cache link-layer address, + * on reception of inbound ND6 packets. (RS/RA/NS/redirect) + */ +struct rtentry * +nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code) + struct ifnet *ifp; + struct in6_addr *from; + char *lladdr; + int lladdrlen; + int type; /* ICMP6 type */ + int code; /* type dependent information */ +{ + struct rtentry *rt = NULL; + struct llinfo_nd6 *ln = NULL; + int is_newentry; + struct sockaddr_dl *sdl = NULL; + int do_update; + int olladdr; + int llchange; + int newstate = 0; + + if (!ifp) + panic("ifp == NULL in nd6_cache_lladdr"); + if (!from) + panic("from == NULL in nd6_cache_lladdr"); + + /* nothing must be updated for unspecified address */ + if (IN6_IS_ADDR_UNSPECIFIED(from)) + return NULL; + + /* + * Validation about ifp->if_addrlen and lladdrlen must be done in + * the caller. + * + * XXX If the link does not have link-layer adderss, what should + * we do? (ifp->if_addrlen == 0) + * Spec says nothing in sections for RA, RS and NA. There's small + * description on it in NS section (RFC 2461 7.2.3). + */ + + rt = nd6_lookup(from, 0, ifp); + if (!rt) { + rt = nd6_lookup(from, 1, ifp); + is_newentry = 1; + } else + is_newentry = 0; + + if (!rt) + return NULL; + if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { +fail: + nd6_free(rt); + return NULL; + } + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + if (!ln) + goto fail; + if (!rt->rt_gateway) + goto fail; + if (rt->rt_gateway->sa_family != AF_LINK) + goto fail; + sdl = SDL(rt->rt_gateway); + + olladdr = (sdl->sdl_alen) ? 1 : 0; + if (olladdr && lladdr) { + if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen)) + llchange = 1; + else + llchange = 0; + } else + llchange = 0; + + /* + * newentry olladdr lladdr llchange (*=record) + * 0 n n -- (1) + * 0 y n -- (2) + * 0 n y -- (3) * STALE + * 0 y y n (4) * + * 0 y y y (5) * STALE + * 1 -- n -- (6) NOSTATE(= PASSIVE) + * 1 -- y -- (7) * STALE + */ + + if (lladdr) { /*(3-5) and (7)*/ + /* + * Record source link-layer address + * XXX is it dependent to ifp->if_type? + */ + sdl->sdl_alen = ifp->if_addrlen; + bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); + } + + if (!is_newentry) { + if ((!olladdr && lladdr) /*(3)*/ + || (olladdr && lladdr && llchange)) { /*(5)*/ + do_update = 1; + newstate = ND6_LLINFO_STALE; + } else /*(1-2,4)*/ + do_update = 0; + } else { + do_update = 1; + if (!lladdr) /*(6)*/ + newstate = ND6_LLINFO_NOSTATE; + else /*(7)*/ + newstate = ND6_LLINFO_STALE; + } + + if (do_update) { + /* + * Update the state of the neighbor cache. + */ + ln->ln_state = newstate; + + if (ln->ln_state == ND6_LLINFO_STALE) { + rt->rt_flags &= ~RTF_REJECT; + if (ln->ln_hold) { + nd6_output(ifp, ln->ln_hold, + (struct sockaddr_in6 *)rt_key(rt), + rt); + ln->ln_hold = 0; + } + } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { + /* probe right away */ + ln->ln_expire = time_second; + } + } + + /* + * ICMP6 type dependent behavior. + * + * NS: clear IsRouter if new entry + * RS: clear IsRouter + * RA: set IsRouter if there's lladdr + * redir: clear IsRouter if new entry + * + * RA case, (1): + * The spec says that we must set IsRouter in the following cases: + * - If lladdr exist, set IsRouter. This means (1-5). + * - If it is old entry (!newentry), set IsRouter. This means (7). + * So, based on the spec, in (1-5) and (7) cases we must set IsRouter. + * A quetion arises for (1) case. (1) case has no lladdr in the + * neighbor cache, this is similar to (6). + * This case is rare but we figured that we MUST NOT set IsRouter. + * + * newentry olladdr lladdr llchange NS RS RA redir + * D R + * 0 n n -- (1) c ? s + * 0 y n -- (2) c s s + * 0 n y -- (3) c s s + * 0 y y n (4) c s s + * 0 y y y (5) c s s + * 1 -- n -- (6) c c c s + * 1 -- y -- (7) c c s c s + * + * (c=clear s=set) + */ + switch (type & 0xff) { + case ND_NEIGHBOR_SOLICIT: + /* + * New entry must have is_router flag cleared. + */ + if (is_newentry) /*(6-7)*/ + ln->ln_router = 0; + break; + case ND_REDIRECT: + /* + * If the icmp is a redirect to a better router, always set the + * is_router flag. Otherwise, if the entry is newly created, + * clear the flag. [RFC 2461, sec 8.3] + * + */ + if (code == ND_REDIRECT_ROUTER) + ln->ln_router = 1; + else if (is_newentry) /*(6-7)*/ + ln->ln_router = 0; + break; + case ND_ROUTER_SOLICIT: + /* + * is_router flag must always be cleared. + */ + ln->ln_router = 0; + break; + case ND_ROUTER_ADVERT: + /* + * Mark an entry with lladdr as a router. + */ + if ((!is_newentry && (olladdr || lladdr)) /*(2-5)*/ + || (is_newentry && lladdr)) { /*(7)*/ + ln->ln_router = 1; + } + break; + } + + return rt; +} + +static void +nd6_slowtimo(ignored_arg) + void *ignored_arg; +{ + int s = splnet(); + register int i; + register struct nd_ifinfo *nd6if; + + timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); + for (i = 1; i < if_index + 1; i++) { + nd6if = &nd_ifinfo[i]; + if (nd6if->basereachable && /* already initialized */ + (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) { + /* + * Since reachable time rarely changes by router + * advertisements, we SHOULD insure that a new random + * value gets recomputed at least once every few hours. + * (RFC 2461, 6.3.4) + */ + nd6if->recalctm = nd6_recalc_reachtm_interval; + nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable); + } + } + splx(s); +} + +#define senderr(e) { error = (e); goto bad;} +int +nd6_output(ifp, m0, dst, rt0) + register struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr_in6 *dst; + struct rtentry *rt0; +{ + register struct mbuf *m = m0; + register struct rtentry *rt = rt0; + struct llinfo_nd6 *ln = NULL; + int error = 0; + + if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) + goto sendpkt; + + /* + * XXX: we currently do not make neighbor cache on any interface + * other than ARCnet, Ethernet and FDDI. + */ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + break; + default: + goto sendpkt; + } + + /* + * next hop determination. This routine is derived from ether_outpout. + */ + if (rt) { + if ((rt->rt_flags & RTF_UP) == 0) { + if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1, 0UL)) != + NULL) + { + rt->rt_refcnt--; + if (rt->rt_ifp != ifp) + return nd6_output(ifp, m0, dst, rt); /* XXX: loop care? */ + } else + senderr(EHOSTUNREACH); + } + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_gwroute == 0) + goto lookup; + if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { + rtfree(rt); rt = rt0; + lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL); + if ((rt = rt->rt_gwroute) == 0) + senderr(EHOSTUNREACH); + } + } + if (rt->rt_flags & RTF_REJECT) + senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); + } + + /* + * Address resolution or Neighbor Unreachability Detection + * for the next hop. + * At this point, the destination of the packet must be a unicast + * or an anycast address(i.e. not a multicast). + */ + + /* Look up the neighbor cache for the nexthop */ + if (rt && (rt->rt_flags & RTF_LLINFO) != 0) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + else { + if ((rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + } + if (!ln || !rt) { + log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s " + "(ln=%p, rt=%p)\n", + ip6_sprintf(&dst->sin6_addr), ln, rt); + senderr(EIO); /* XXX: good error? */ + } + + + /* + * The first time we send a packet to a neighbor whose entry is + * STALE, we have to change the state to DELAY and a sets a timer to + * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do + * neighbor unreachability detection on expiration. + * (RFC 2461 7.3.3) + */ + if (ln->ln_state == ND6_LLINFO_STALE) { + ln->ln_asked = 0; + ln->ln_state = ND6_LLINFO_DELAY; + ln->ln_expire = time_second + nd6_delay; + } + + /* + * If the neighbor cache entry has a state other than INCOMPLETE + * (i.e. its link-layer address is already reloved), just + * send the packet. + */ + if (ln->ln_state > ND6_LLINFO_INCOMPLETE) + goto sendpkt; + + /* + * There is a neighbor cache entry, but no ethernet address + * response yet. Replace the held mbuf (if any) with this + * latest one. + * + * XXX Does the code conform to rate-limiting rule? + * (RFC 2461 7.2.2) + */ + if (ln->ln_state == ND6_LLINFO_WAITDELETE || + ln->ln_state == ND6_LLINFO_NOSTATE) + ln->ln_state = ND6_LLINFO_INCOMPLETE; + if (ln->ln_hold) + m_freem(ln->ln_hold); + ln->ln_hold = m; + if (ln->ln_expire) { + rt->rt_flags &= ~RTF_REJECT; + if (ln->ln_asked < nd6_mmaxtries && + ln->ln_expire < time_second) { + ln->ln_asked++; + ln->ln_expire = time_second + + nd_ifinfo[ifp->if_index].retrans / 1000; + nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); + } + } + return(0); + + sendpkt: + return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt)); + + bad: + if (m) + m_freem(m); + return (error); +} +#undef senderr + +int +nd6_storelladdr(ifp, rt, m, dst, desten) + struct ifnet *ifp; + struct rtentry *rt; + struct mbuf *m; + struct sockaddr *dst; + u_char *desten; +{ + struct sockaddr_dl *sdl; + + if (m->m_flags & M_MCAST) { + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: + ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, + desten); + return(1); + break; + case IFT_ARCNET: + *desten = 0; + return(1); + default: + return(0); + } + } + + if (rt == NULL || + rt->rt_gateway->sa_family != AF_LINK) { + printf("nd6_storelladdr: something odd happens\n"); + return(0); + } + sdl = SDL(rt->rt_gateway); + if (sdl->sdl_alen != 0) + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); + + return(1); +} diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h new file mode 100644 index 0000000..2bb4fe4 --- /dev/null +++ b/sys/netinet6/nd6.h @@ -0,0 +1,302 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETINET6_ND6_H_ +#define _NETINET6_ND6_H_ + +#include + +struct llinfo_nd6 { + struct llinfo_nd6 *ln_next; + struct llinfo_nd6 *ln_prev; + struct rtentry *ln_rt; + struct mbuf *ln_hold; /* last packet until resolved/timeout */ + long ln_asked; /* number of queries already sent for this addr */ + u_long ln_expire; /* lifetime for NDP state transition */ + short ln_state; /* reachability state */ + short ln_router; /* 2^0: ND6 router bit */ +}; + +#define ND6_LLINFO_NOSTATE -2 +#define ND6_LLINFO_WAITDELETE -1 +#define ND6_LLINFO_INCOMPLETE 0 +#define ND6_LLINFO_REACHABLE 1 +#define ND6_LLINFO_STALE 2 +#define ND6_LLINFO_DELAY 3 +#define ND6_LLINFO_PROBE 4 + +struct nd_ifinfo { + u_int32_t linkmtu; /* LinkMTU */ + u_int32_t maxmtu; /* Upper bound of LinkMTU */ + u_int32_t basereachable; /* BaseReachableTime */ + u_int32_t reachable; /* Reachable Time */ + u_int32_t retrans; /* Retrans Timer */ + int recalctm; /* BaseReacable re-calculation timer */ + u_int8_t chlim; /* CurHopLimit */ + u_int8_t receivedra; +}; + +struct in6_nbrinfo { + char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ + struct in6_addr addr; /* IPv6 address of the neighbor */ + long asked; /* number of queries already sent for this addr */ + int isrouter; /* if it acts as a router */ + int state; /* reachability state */ + int expire; /* lifetime for NDP state transition */ +}; + +#define DRLSTSIZ 10 +#define PRLSTSIZ 10 +struct in6_drlist { + char ifname[IFNAMSIZ]; + struct { + struct in6_addr rtaddr; + u_char flags; + u_short rtlifetime; + u_long expire; + u_short if_index; + } defrouter[DRLSTSIZ]; +}; + +struct in6_prlist { + char ifname[IFNAMSIZ]; + struct { + struct in6_addr prefix; + struct prf_ra raflags; + u_char prefixlen; + u_long vltime; + u_long pltime; + u_long expire; + u_short if_index; + u_short advrtrs; /* number of advertisement routers */ + struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */ + } prefix[PRLSTSIZ]; +}; + +struct in6_ndireq { + char ifname[IFNAMSIZ]; + struct nd_ifinfo ndi; +}; + +/* protocol constants */ +#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ +#define RTR_SOLICITATION_INTERVAL 4 /*4sec*/ +#define MAX_RTR_SOLICITATIONS 3 + +#define ND6_INFINITE_LIFETIME 0xffffffff + +#ifdef _KERNEL +/* node constants */ +#define MAX_REACHABLE_TIME 3600000 /* msec */ +#define REACHABLE_TIME 30000 /* msec */ +#define RETRANS_TIMER 1000 /* msec */ +#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ +#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ +#define ND_COMPUTE_RTIME(x) \ + (((MIN_RANDOM_FACTOR * (x >> 10)) + (random() & \ + ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) + +struct nd_defrouter { + LIST_ENTRY(nd_defrouter) dr_entry; +#define dr_next dr_entry.le_next + struct in6_addr rtaddr; + u_char flags; + u_short rtlifetime; + u_long expire; + struct ifnet *ifp; +}; + +struct nd_prefix { + struct ifnet *ndpr_ifp; + LIST_ENTRY(nd_prefix) ndpr_entry; + struct sockaddr_in6 ndpr_prefix; /* prefix */ + struct in6_addr ndpr_mask; /* netmask derived from the prefix */ + struct in6_addr ndpr_addr; /* address that is derived from the prefix */ + u_int32_t ndpr_vltime; /* advertised valid lifetime */ + u_int32_t ndpr_pltime; /* advertised preferred lifetime */ + time_t ndpr_expire; /* expiration time of the prefix */ + time_t ndpr_preferred; /* preferred time of the prefix */ + struct prf_ra ndpr_flags; + /* list of routers that advertise the prefix: */ + LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; + u_char ndpr_plen; + struct ndpr_stateflags { + /* if this prefix can be regarded as on-link */ + u_char onlink : 1; + } ndpr_stateflags; +}; + +#define ndpr_next ndpr_entry.le_next + +#define ndpr_raf ndpr_flags +#define ndpr_raf_onlink ndpr_flags.onlink +#define ndpr_raf_auto ndpr_flags.autonomous + +#define ndpr_statef_onlink ndpr_stateflags.onlink +#define ndpr_statef_addmark ndpr_stateflags.addmark + +/* + * We keep expired prefix for certain amount of time, for validation purposes. + * 1800s = MaxRtrAdvInterval + */ +#define NDPR_KEEP_EXPIRED (1800 * 2) + +/* + * Message format for use in obtaining information about prefixes + * from inet6 sysctl function + */ +struct inet6_ndpr_msghdr { + u_short inpm_msglen; /* to skip over non-understood messages */ + u_char inpm_version; /* future binary compatability */ + u_char inpm_type; /* message type */ + struct in6_addr inpm_prefix; + u_long prm_vltim; + u_long prm_pltime; + u_long prm_expire; + u_long prm_preferred; + struct in6_prflags prm_flags; + u_short prm_index; /* index for associated ifp */ + u_char prm_plen; /* length of prefix in bits */ +}; + +#define prm_raf_onlink prm_flags.prf_ra.onlink +#define prm_raf_auto prm_flags.prf_ra.autonomous + +#define prm_statef_onlink prm_flags.prf_state.onlink + +#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid +#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd + +#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr)) +#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr)) + +struct nd_pfxrouter { + LIST_ENTRY(nd_pfxrouter) pfr_entry; +#define pfr_next pfr_entry.le_next + struct nd_defrouter *router; +}; + +LIST_HEAD(nd_drhead, nd_defrouter); +LIST_HEAD(nd_prhead, nd_prefix); + +/* nd6.c */ +extern int nd6_prune; +extern int nd6_delay; +extern int nd6_umaxtries; +extern int nd6_mmaxtries; +extern int nd6_useloopback; +extern int nd6_proxyall; +extern struct llinfo_nd6 llinfo_nd6; +extern struct nd_ifinfo *nd_ifinfo; +extern struct nd_drhead nd_defrouter; +extern struct nd_prhead nd_prefix; + +union nd_opts { + struct nd_opt_hdr *nd_opt_array[9]; + struct { + struct nd_opt_hdr *zero; + struct nd_opt_hdr *src_lladdr; + struct nd_opt_hdr *tgt_lladdr; + struct nd_opt_prefix_info *pi_beg;/* multiple opts, start */ + struct nd_opt_rd_hdr *rh; + struct nd_opt_mtu *mtu; + struct nd_opt_hdr *search; /* multiple opts */ + struct nd_opt_hdr *last; /* multiple opts */ + int done; + struct nd_opt_prefix_info *pi_end;/* multiple opts, end */ + } nd_opt_each; +}; +#define nd_opts_src_lladdr nd_opt_each.src_lladdr +#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr +#define nd_opts_pi nd_opt_each.pi_beg +#define nd_opts_pi_end nd_opt_each.pi_end +#define nd_opts_rh nd_opt_each.rh +#define nd_opts_mtu nd_opt_each.mtu +#define nd_opts_search nd_opt_each.search +#define nd_opts_last nd_opt_each.last +#define nd_opts_done nd_opt_each.done + +/* XXX: need nd6_var.h?? */ +/* nd6.c */ +void nd6_init __P((void)); +void nd6_ifattach __P((struct ifnet *)); +int nd6_is_addr_neighbor __P((struct in6_addr *, struct ifnet *)); +void nd6_option_init __P((void *, int, union nd_opts *)); +struct nd_opt_hdr *nd6_option __P((union nd_opts *)); +int nd6_options __P((union nd_opts *)); +struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *)); +void nd6_setmtu __P((struct ifnet *)); +void nd6_timer __P((void *)); +void nd6_free __P((struct rtentry *)); +void nd6_nud_hint __P((struct rtentry *, struct in6_addr *)); +int nd6_resolve __P((struct ifnet *, struct rtentry *, + struct mbuf *, struct sockaddr *, u_char *)); +void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +int nd6_ioctl __P((u_long, caddr_t, struct ifnet *)); +struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *, + char *, int, int, int)); +/* for test */ +int nd6_output __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *, + struct rtentry *)); +int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, + struct sockaddr *, u_char *)); + +/* nd6_nbr.c */ +void nd6_na_input __P((struct mbuf *, int, int)); +void nd6_na_output __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, u_long, int)); +void nd6_ns_input __P((struct mbuf *, int, int)); +void nd6_ns_output __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, struct llinfo_nd6 *, int)); +caddr_t nd6_ifptomac __P((struct ifnet *)); +void nd6_dad_start __P((struct ifaddr *, int *)); +void nd6_dad_duplicated __P((struct ifaddr *)); + +/* nd6_rtr.c */ +void nd6_rs_input __P((struct mbuf *, int, int)); +void nd6_ra_input __P((struct mbuf *, int, int)); +void prelist_del __P((struct nd_prefix *)); +void defrouter_addreq __P((struct nd_defrouter *)); +void defrouter_delreq __P((struct nd_defrouter *, int)); +void defrtrlist_del __P((struct nd_defrouter *)); +void prelist_remove __P((struct nd_prefix *)); +int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, + struct mbuf *)); +struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, + struct ifnet *)); +int in6_ifdel __P((struct ifnet *, struct in6_addr *)); +int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); +void rt6_flush __P((struct in6_addr *, struct ifnet *)); + +#endif /* _KERNEL */ + +#endif /* _NETINET6_ND6_H_ */ diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c new file mode 100644 index 0000000..2836d48 --- /dev/null +++ b/sys/netinet6/nd6_nbr.c @@ -0,0 +1,1123 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_inet.h" +#include "opt_ipsec.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SDL(s) ((struct sockaddr_dl *)s) + +struct dadq; +static struct dadq *nd6_dad_find __P((struct ifaddr *)); +static void nd6_dad_timer __P((struct ifaddr *)); +static void nd6_dad_ns_input __P((struct ifaddr *)); +static void nd6_dad_na_input __P((struct ifaddr *)); + +/* ignore NS in DAD - specwise incorrect, */ +int dad_ignore_ns = 0; + +/* + * Input an Neighbor Solicitation Message. + * + * Based on RFC 2461 + * Based on RFC 2462 (duplicated address detection) + * + * XXX proxy advertisement + */ +void +nd6_ns_input(m, off, icmp6len) + struct mbuf *m; + int off, icmp6len; +{ + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct nd_neighbor_solicit *nd_ns + = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); + struct in6_addr saddr6 = ip6->ip6_src; + struct in6_addr daddr6 = ip6->ip6_dst; + struct in6_addr taddr6 = nd_ns->nd_ns_target; + struct in6_addr myaddr6; + char *lladdr = NULL; + struct ifaddr *ifa; + int lladdrlen = 0; + int anycast = 0, proxy = 0, tentative = 0; + int tlladdr; + union nd_opts ndopts; + + if (ip6->ip6_hlim != 255) { + log(LOG_ERR, + "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim); + return; + } + + if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { + /* dst has to be solicited node multicast address. */ + if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL + /*don't check ifindex portion*/ + && daddr6.s6_addr32[1] == 0 + && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE + && daddr6.s6_addr8[12] == 0xff) { + ; /*good*/ + } else { + log(LOG_INFO, "nd6_ns_input: bad DAD packet " + "(wrong ip6 dst)\n"); + goto bad; + } + } + + if (IN6_IS_ADDR_MULTICAST(&taddr6)) { + log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"); + goto bad; + } + + if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) + taddr6.s6_addr16[1] = htons(ifp->if_index); + + icmp6len -= sizeof(*nd_ns); + nd6_option_init(nd_ns + 1, icmp6len, &ndopts); + if (nd6_options(&ndopts) < 0) { + log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n"); + goto bad; + } + + if (ndopts.nd_opts_src_lladdr) { + lladdr = (char *)(ndopts.nd_opts_src_lladdr +1); + lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; + } + + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { + log(LOG_INFO, "nd6_ns_input: bad DAD packet " + "(link-layer address option)\n"); + goto bad; + } + + /* + * Attaching target link-layer address to the NA? + * (RFC 2461 7.2.4) + * + * NS IP dst is unicast/anycast MUST NOT add + * NS IP dst is solicited-node multicast MUST add + * + * In implementation, we add target link-layer address by default. + * We do not add one in MUST NOT cases. + */ + if (!IN6_IS_ADDR_MULTICAST(&daddr6)) + tlladdr = 0; + else + tlladdr = 1; + + /* + * Target address (taddr6) must be either: + * (1) Valid unicast/anycast address for my receiving interface, + * (2) Unicast address for which I'm offering proxy service, or + * (3) "tentative" address on which DAD is being performed. + */ + /* (1) and (3) check. */ + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); + + /* (2) check. */ + if (!ifa && nd6_proxyall) { + struct rtentry *rt; + struct sockaddr_in6 tsin6; + + bzero(&tsin6, sizeof tsin6); + tsin6.sin6_len = sizeof(struct sockaddr_in6); + tsin6.sin6_family = AF_INET6; + tsin6.sin6_addr = taddr6; + + rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0); + if (rt && rt->rt_ifp != ifp) { + /* + * search link local addr for ifp, and use it for + * proxy NA. + */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + if (ifa) + proxy = 1; + } + rtfree(rt); + } + if (!ifa) { + /* + * We've got a NS packet, and we don't have that adddress + * assigned for us. We MUST silently ignore it. + * See RFC2461 7.2.3. + */ + return; + } + myaddr6 = *IFA_IN6(ifa); + anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; + tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) + return; + + if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { + log(LOG_INFO, + "nd6_ns_input: lladdrlen mismatch for %s " + "(if %d, NS packet %d)\n", + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + } + + if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { + log(LOG_INFO, + "nd6_ns_input: duplicate IP6 address %s\n", + ip6_sprintf(&saddr6)); + return; + } + + /* + * We have neighbor solicitation packet, with target address equals to + * one of my tentative address. + * + * src addr how to process? + * --- --- + * multicast of course, invalid (rejected in ip6_input) + * unicast somebody is doing address resolution -> ignore + * unspec dup address detection + * + * The processing is defined in RFC 2462. + */ + if (tentative) { + /* + * If source address is unspecified address, it is for + * duplicated address detection. + * + * If not, the packet is for addess resolution; + * silently ignore it. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) + nd6_dad_ns_input(ifa); + + return; + } + + /* + * If the source address is unspecified address, entries must not + * be created or updated. + * It looks that sender is performing DAD. Output NA toward + * all-node multicast address, to tell the sender that I'm using + * the address. + * S bit ("solicited") must be zero. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { + saddr6 = in6addr_linklocal_allnodes; + saddr6.s6_addr16[1] = htons(ifp->if_index); + nd6_na_output(ifp, &saddr6, &taddr6, + ((anycast || proxy || !tlladdr) + ? 0 : ND_NA_FLAG_OVERRIDE) + | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), + tlladdr); + return; + } + + nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); + + nd6_na_output(ifp, &saddr6, &taddr6, + ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) + | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) + | ND_NA_FLAG_SOLICITED, + tlladdr); + return; + + bad: + log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)); + log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)); + log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)); + return; +} + +/* + * Output an Neighbor Solicitation Message. Caller specifies: + * - ICMP6 header source IP6 address + * - ND6 header target IP6 address + * - ND6 header source datalink address + * + * Based on RFC 2461 + * Based on RFC 2462 (duplicated address detection) + */ +void +nd6_ns_output(ifp, daddr6, taddr6, ln, dad) + struct ifnet *ifp; + struct in6_addr *daddr6, *taddr6; + struct llinfo_nd6 *ln; /* for source address determination */ + int dad; /* duplicated address detection */ +{ + struct mbuf *m; + struct ip6_hdr *ip6; + struct nd_neighbor_solicit *nd_ns; + struct in6_ifaddr *ia = NULL; + struct ip6_moptions im6o; + int icmp6len; + caddr_t mac; + struct ifnet *outif = NULL; + + if (IN6_IS_ADDR_MULTICAST(taddr6)) + return; + + if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) + return; + + if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { + m->m_flags |= M_MCAST; + im6o.im6o_multicast_ifp = ifp; + im6o.im6o_multicast_hlim = 255; + im6o.im6o_multicast_loop = 0; + } + + icmp6len = sizeof(*nd_ns); + m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; + MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enought. but just in case */ + + /* fill neighbor solicitation packet */ + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = 0; + ip6->ip6_vfc = IPV6_VERSION; + /* ip6->ip6_plen will be set later */ + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 255; + if (daddr6) + ip6->ip6_dst = *daddr6; + else { + ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; + ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); + ip6->ip6_dst.s6_addr32[1] = 0; + ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; + ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; + ip6->ip6_dst.s6_addr8[12] = 0xff; + } + if (!dad) { + /* spec-wise correct, scope match */ + /* + * RFC2461 7.2.2: + * "If the source address of the packet prompting the + * solicitation is the same as one of the addresses assigned + * to the outgoing interface, that address SHOULD be placed + * in the IP Source Address of the outgoing solicitation. + * Otherwise, any one of the addresses assigned to the + * interface should be used." + * + * We use the source address for the prompting packet + * (saddr6), if: + * - saddr6 is given from the caller (by giving "ln"), and + * - saddr6 belongs to the outgoing interface. + * Otherwise, we perform a scope-wise match. + */ + struct ip6_hdr *hip6; /*hold ip6*/ + struct in6_addr *saddr6; + + if (ln && ln->ln_hold) { + hip6 = mtod(ln->ln_hold, struct ip6_hdr *); + /* XXX pullup? */ + if (sizeof(*hip6) < ln->ln_hold->m_len) + saddr6 = &hip6->ip6_src; + else + saddr6 = NULL; + } else + saddr6 = NULL; + if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6)) + bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6)); + else { + ia = in6_ifawithifp(ifp, &ip6->ip6_dst); + if (ia == NULL) { + m_freem(m); /*XXX*/ + return; + } + ip6->ip6_src = ia->ia_addr.sin6_addr; + } + } else { + /* + * Source address for DAD packet must always be IPv6 + * unspecified address. (0::0) + */ + bzero(&ip6->ip6_src, sizeof(ip6->ip6_src)); + } + nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1); + nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; + nd_ns->nd_ns_code = 0; + nd_ns->nd_ns_reserved = 0; + nd_ns->nd_ns_target = *taddr6; + + if (IN6_IS_SCOPE_LINKLOCAL(&nd_ns->nd_ns_target)) + nd_ns->nd_ns_target.s6_addr16[1] = 0; + + /* + * Add source link-layer address option. + * + * spec implementation + * --- --- + * DAD packet MUST NOT do not add the option + * there's no link layer address: + * impossible do not add the option + * there's link layer address: + * Multicast NS MUST add one add the option + * Unicast NS SHOULD add one add the option + */ + if (!dad && (mac = nd6_ifptomac(ifp))) { + int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); + /* 8 byte alignments... */ + optlen = (optlen + 7) & ~7; + + m->m_pkthdr.len += optlen; + m->m_len += optlen; + icmp6len += optlen; + bzero((caddr_t)nd_opt, optlen); + nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; + nd_opt->nd_opt_len = optlen >> 3; + bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); + } + + ip6->ip6_plen = htons((u_short)icmp6len); + nd_ns->nd_ns_cksum = 0; + nd_ns->nd_ns_cksum + = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); + +#ifdef IPSEC + m->m_pkthdr.rcvif = NULL; +#endif /*IPSEC*/ + ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); + if (outif) { + icmp6_ifstat_inc(outif, ifs6_out_msg); + icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit); + } + icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++; +} + +/* + * Neighbor advertisement input handling. + * + * Based on RFC 2461 + * Based on RFC 2462 (duplicated address detection) + */ +void +nd6_na_input(m, off, icmp6len) + struct mbuf *m; + int off, icmp6len; +{ + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct nd_neighbor_advert *nd_na + = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); + struct in6_addr daddr6 = ip6->ip6_dst; + struct in6_addr taddr6 = nd_na->nd_na_target; + int flags = nd_na->nd_na_flags_reserved; + int is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); + int is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); + int is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); + char *lladdr = NULL; + int lladdrlen = 0; + struct ifaddr *ifa; + struct llinfo_nd6 *ln; + struct rtentry *rt; + struct sockaddr_dl *sdl; + union nd_opts ndopts; + + if (ip6->ip6_hlim != 255) { + log(LOG_ERR, + "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim); + return; + } + + if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) + taddr6.s6_addr16[1] = htons(ifp->if_index); + + if (IN6_IS_ADDR_MULTICAST(&taddr6)) { + log(LOG_ERR, + "nd6_na_input: invalid target address %s\n", + ip6_sprintf(&taddr6)); + return; + } + if (IN6_IS_ADDR_MULTICAST(&daddr6)) + if (is_solicited) { + log(LOG_ERR, + "nd6_na_input: a solicited adv is multicasted\n"); + return; + } + + icmp6len -= sizeof(*nd_na); + nd6_option_init(nd_na + 1, icmp6len, &ndopts); + if (nd6_options(&ndopts) < 0) { + log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n"); + return; + } + + if (ndopts.nd_opts_tgt_lladdr) { + lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); + lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; + } + + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); + + /* + * Target address matches one of my interface address. + * + * If my address is tentative, this means that there's somebody + * already using the same address as mine. This indicates DAD failure. + * This is defined in RFC 2462. + * + * Otherwise, process as defined in RFC 2461. + */ + if (ifa + && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { + nd6_dad_na_input(ifa); + return; + } + + /* Just for safety, maybe unnecessery. */ + if (ifa) { + log(LOG_ERR, + "nd6_na_input: duplicate IP6 address %s\n", + ip6_sprintf(&taddr6)); + return; + } + + if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { + log(LOG_INFO, + "nd6_na_input: lladdrlen mismatch for %s " + "(if %d, NA packet %d)\n", + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + } + + /* + * If no neighbor cache entry is found, NA SHOULD silently be discarded. + */ + rt = nd6_lookup(&taddr6, 0, ifp); + if ((rt == NULL) || + ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) || + ((sdl = SDL(rt->rt_gateway)) == NULL)) + return; + + if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { + /* + * If the link-layer has address, and no lladdr option came, + * discard the packet. + */ + if (ifp->if_addrlen && !lladdr) + return; + + /* + * Record link-layer address, and update the state. + */ + sdl->sdl_alen = ifp->if_addrlen; + bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); + if (is_solicited) { + ln->ln_state = ND6_LLINFO_REACHABLE; + if (ln->ln_expire) + ln->ln_expire = time_second + + nd_ifinfo[rt->rt_ifp->if_index].reachable; + } else + ln->ln_state = ND6_LLINFO_STALE; + ln->ln_router = is_router; + } else { + int llchange; + + /* + * Check if the link-layer address has changed or not. + */ + if (!lladdr) + llchange = 0; + else { + if (sdl->sdl_alen) { + if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen)) + llchange = 1; + else + llchange = 0; + } else + llchange = 1; + } + + /* + * This is VERY complex. Look at it with care. + * + * override solicit lladdr llchange action + * (L: record lladdr) + * + * 0 0 n -- (2c) + * 0 0 y n (2b) L + * 0 0 y y (1) REACHABLE->STALE + * 0 1 n -- (2c) *->REACHABLE + * 0 1 y n (2b) L *->REACHABLE + * 0 1 y y (1) REACHABLE->STALE + * 1 0 n -- (2a) + * 1 0 y n (2a) L + * 1 0 y y (2a) L *->STALE + * 1 1 n -- (2a) *->REACHABLE + * 1 1 y n (2a) L *->REACHABLE + * 1 1 y y (2a) L *->REACHABLE + */ + if (!is_override && (lladdr && llchange)) { /* (1) */ + /* + * If state is REACHABLE, make it STALE. + * no other updates should be done. + */ + if (ln->ln_state == ND6_LLINFO_REACHABLE) + ln->ln_state = ND6_LLINFO_STALE; + return; + } else if (is_override /* (2a) */ + || (!is_override && (lladdr && !llchange)) /* (2b) */ + || !lladdr) { /* (2c) */ + /* + * Update link-local address, if any. + */ + if (lladdr) { + sdl->sdl_alen = ifp->if_addrlen; + bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); + } + + /* + * If solicited, make the state REACHABLE. + * If not solicited and the link-layer address was + * changed, make it STALE. + */ + if (is_solicited) { + ln->ln_state = ND6_LLINFO_REACHABLE; + if (ln->ln_expire) { + ln->ln_expire = time_second + + nd_ifinfo[ifp->if_index].reachable; + } + } else { + if (lladdr && llchange) + ln->ln_state = ND6_LLINFO_STALE; + } + } + + if (ln->ln_router && !is_router) { + /* + * The peer dropped the router flag. + * Remove the sender from the Default Router List and + * update the Destination Cache entries. + */ + struct nd_defrouter *dr; + struct in6_addr *in6; + int s; + + in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + s = splnet(); + dr = defrouter_lookup(in6, rt->rt_ifp); + if (dr) + defrtrlist_del(dr); + else if (!ip6_forwarding && ip6_accept_rtadv) { + /* + * Even if the neighbor is not in the default + * router list, the neighbor may be used + * as a next hop for some destinations + * (e.g. redirect case). So we must + * call rt6_flush explicitly. + */ + rt6_flush(&ip6->ip6_src, rt->rt_ifp); + } + splx(s); + } + ln->ln_router = is_router; + } + rt->rt_flags &= ~RTF_REJECT; + ln->ln_asked = 0; + if (ln->ln_hold) { + nd6_output(ifp, ln->ln_hold, + (struct sockaddr_in6 *)rt_key(rt), rt); + ln->ln_hold = 0; + } +} + +/* + * Neighbor advertisement output handling. + * + * Based on RFC 2461 + * + * XXX NA delay for anycast address is not implemented yet + * (RFC 2461 7.2.7) + * XXX proxy advertisement? + */ +void +nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) + struct ifnet *ifp; + struct in6_addr *daddr6, *taddr6; + u_long flags; + int tlladdr; /* 1 if include target link-layer address */ +{ + struct mbuf *m; + struct ip6_hdr *ip6; + struct nd_neighbor_advert *nd_na; + struct in6_ifaddr *ia = NULL; + struct ip6_moptions im6o; + int icmp6len; + caddr_t mac; + struct ifnet *outif; + + if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) + return; + + if (IN6_IS_ADDR_MULTICAST(daddr6)) { + m->m_flags |= M_MCAST; + im6o.im6o_multicast_ifp = ifp; + im6o.im6o_multicast_hlim = 255; + im6o.im6o_multicast_loop = 0; + } + + icmp6len = sizeof(*nd_na); + m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; + MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enough. but just in case */ + + /* fill neighbor advertisement packet */ + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = 0; + ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 255; + if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) { + /* reply to DAD */ + ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; + ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); + ip6->ip6_dst.s6_addr32[1] = 0; + ip6->ip6_dst.s6_addr32[2] = 0; + ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE; + flags &= ~ND_NA_FLAG_SOLICITED; + } else + ip6->ip6_dst = *daddr6; + + /* + * Select a source whose scope is the same as that of the dest. + */ + ia = in6_ifawithifp(ifp, &ip6->ip6_dst); + if (ia == NULL) { + m_freem(m); + return; + } + ip6->ip6_src = ia->ia_addr.sin6_addr; + nd_na = (struct nd_neighbor_advert *)(ip6 + 1); + nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; + nd_na->nd_na_code = 0; + nd_na->nd_na_target = *taddr6; + if (IN6_IS_SCOPE_LINKLOCAL(&nd_na->nd_na_target)) + nd_na->nd_na_target.s6_addr16[1] = 0; + + /* + * "tlladdr" indicates NS's condition for adding tlladdr or not. + * see nd6_ns_input() for details. + * Basically, if NS packet is sent to unicast/anycast addr, + * target lladdr option SHOULD NOT be included. + */ + if (tlladdr && (mac = nd6_ifptomac(ifp))) { + int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); + + /* roundup to 8 bytes alignment! */ + optlen = (optlen + 7) & ~7; + + m->m_pkthdr.len += optlen; + m->m_len += optlen; + icmp6len += optlen; + bzero((caddr_t)nd_opt, optlen); + nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; + nd_opt->nd_opt_len = optlen >> 3; + bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); + } else + flags &= ~ND_NA_FLAG_OVERRIDE; + + ip6->ip6_plen = htons((u_short)icmp6len); + nd_na->nd_na_flags_reserved = flags; + nd_na->nd_na_cksum = 0; + nd_na->nd_na_cksum = + in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); + +#ifdef IPSEC + m->m_pkthdr.rcvif = NULL; +#endif /*IPSEC*/ + ip6_output(m, NULL, NULL, 0, &im6o, &outif); + if (outif) { + icmp6_ifstat_inc(outif, ifs6_out_msg); + icmp6_ifstat_inc(outif, ifs6_out_neighboradvert); + } + icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++; +} + +caddr_t +nd6_ifptomac(ifp) + struct ifnet *ifp; +{ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + return ((caddr_t)(ifp + 1)); + break; + default: + return NULL; + } +} + +TAILQ_HEAD(dadq_head, dadq); +struct dadq { + TAILQ_ENTRY(dadq) dad_list; + struct ifaddr *dad_ifa; + int dad_count; /* max NS to send */ + int dad_ns_ocount; /* NS sent so far */ + int dad_ns_icount; + int dad_na_icount; + struct callout_handle dad_timer; +}; + +static struct dadq_head dadq; + +static struct dadq * +nd6_dad_find(ifa) + struct ifaddr *ifa; +{ + struct dadq *dp; + + for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) { + if (dp->dad_ifa == ifa) + return dp; + } + return NULL; +} + +/* + * Start Duplicated Address Detection (DAD) for specified interface address. + */ +void +nd6_dad_start(ifa, tick) + struct ifaddr *ifa; + int *tick; /* minimum delay ticks for IFF_UP event */ +{ + struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; + struct dadq *dp; + static int dad_init = 0; + + if (!dad_init) { + TAILQ_INIT(&dadq); + dad_init++; + } + + /* + * If we don't need DAD, don't do it. + * There are several cases: + * - DAD is disabled (ip6_dad_count == 0) + * - the interface address is anycast + */ + if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) { + printf("nd6_dad_start: called with non-tentative address " + "%s(%s)\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); + return; + } + if (ia->ia6_flags & IN6_IFF_ANYCAST) { + ia->ia6_flags &= ~IN6_IFF_TENTATIVE; + return; + } + if (!ip6_dad_count) { + ia->ia6_flags &= ~IN6_IFF_TENTATIVE; + return; + } + if (!ifa->ifa_ifp) + panic("nd6_dad_start: ifa->ifa_ifp == NULL"); + if (!(ifa->ifa_ifp->if_flags & IFF_UP)) + return; + if (nd6_dad_find(ifa) != NULL) { + /* DAD already in progress */ + return; + } + + dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT); + if (dp == NULL) { + printf("nd6_dad_start: memory allocation failed for " + "%s(%s)\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); + return; + } + bzero(dp, sizeof(*dp)); + TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); + + /* XXXJRT This is probably a purely debugging message. */ + printf("%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), + ip6_sprintf(&ia->ia_addr.sin6_addr)); + + /* + * Send NS packet for DAD, ip6_dad_count times. + * Note that we must delay the first transmission, if this is the + * first packet to be sent from the interface after interface + * (re)initialization. + */ + dp->dad_ifa = ifa; + ifa->ifa_refcnt++; /*just for safety*/ + dp->dad_count = ip6_dad_count; + dp->dad_ns_icount = dp->dad_na_icount = 0; + dp->dad_ns_ocount = 0; + if (!tick) { + dp->dad_ns_ocount++; + nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr, + NULL, 1); + dp->dad_timer = + timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, + nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); + } else { + int ntick; + + if (*tick == 0) + ntick = random() % (MAX_RTR_SOLICITATION_DELAY * hz); + else + ntick = *tick + random() % (hz / 2); + *tick = ntick; + dp->dad_timer = + timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, + ntick); + } +} + +static void +nd6_dad_timer(ifa) + struct ifaddr *ifa; +{ + int s; + struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; + struct dadq *dp; + + s = splnet(); /*XXX*/ + + /* Sanity check */ + if (ia == NULL) { + printf("nd6_dad_timer: called with null parameter\n"); + goto done; + } + dp = nd6_dad_find(ifa); + if (dp == NULL) { + printf("nd6_dad_timer: DAD structure not found\n"); + goto done; + } + if (ia->ia6_flags & IN6_IFF_DUPLICATED) { + printf("nd6_dad_timer: called with duplicated address " + "%s(%s)\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); + goto done; + } + if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) { + printf("nd6_dad_timer: called with non-tentative address " + "%s(%s)\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); + goto done; + } + + /* Need more checks? */ + if (dp->dad_ns_ocount < dp->dad_count) { + /* + * We have more NS to go. Send NS packet for DAD. + */ + dp->dad_ns_ocount++; + nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr, + NULL, 1); + dp->dad_timer = + timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, + nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); + } else { + /* + * We have transmitted sufficient number of DAD packets. + * See what we've got. + */ + int duplicate; + + duplicate = 0; + + if (dp->dad_na_icount) { + /* + * the check is in nd6_dad_na_input(), + * but just in case + */ + duplicate++; + } + + if (dp->dad_ns_icount) { + /* We've seen NS, means DAD has failed. */ + duplicate++; + } + + if (duplicate) { + /* (*dp) will be freed in nd6_dad_duplicated() */ + dp = NULL; + nd6_dad_duplicated(ifa); + } else { + /* + * We are done with DAD. No NA came, no NS came. + * duplicated address found. + */ + ia->ia6_flags &= ~IN6_IFF_TENTATIVE; + + /* XXXJRT This is probably a purely debugging message */ + printf("%s: DAD complete for %s - no duplicates " + "found\n", if_name(ifa->ifa_ifp), + ip6_sprintf(&ia->ia_addr.sin6_addr)); + + TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + free(dp, M_IP6NDP); + dp = NULL; + ifa->ifa_refcnt--; + } + } + +done: + splx(s); +} + +void +nd6_dad_duplicated(ifa) + struct ifaddr *ifa; +{ + struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; + struct dadq *dp; + + dp = nd6_dad_find(ifa); + if (dp == NULL) { + printf("nd6_dad_duplicated: DAD structure not found\n"); + return; + } + + log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, " + "%d NA\n", if_name(ifa->ifa_ifp), + ip6_sprintf(&ia->ia_addr.sin6_addr), + dp->dad_ns_icount, dp->dad_na_icount); + + ia->ia6_flags &= ~IN6_IFF_TENTATIVE; + ia->ia6_flags |= IN6_IFF_DUPLICATED; + + /* We are done with DAD, with duplicated address found. (failure) */ + untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa + , dp->dad_timer); + + printf("%s: DAD complete for %s - duplicate found\n", + if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); + printf("%s: manual intervention required\n", if_name(ifa->ifa_ifp)); + + TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + free(dp, M_IP6NDP); + dp = NULL; + ifa->ifa_refcnt--; +} + +void +nd6_dad_ns_input(ifa) + struct ifaddr *ifa; +{ + struct in6_ifaddr *ia; + struct ifnet *ifp; + struct in6_addr *taddr6; + struct dadq *dp; + int duplicate; + + if (!ifa) + panic("ifa == NULL in nd6_dad_ns_input"); + + ia = (struct in6_ifaddr *)ifa; + ifp = ifa->ifa_ifp; + taddr6 = &ia->ia_addr.sin6_addr; + duplicate = 0; + dp = nd6_dad_find(ifa); + + /* + * If it is from myself, ignore this. + */ + if (ifp && (ifp->if_flags & IFF_LOOPBACK)) + return; + + /* Quickhack - completely ignore DAD NS packets */ + if (dad_ignore_ns) { + log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for " + "address %s(%s)\n", ip6_sprintf(taddr6), + if_name(ifa->ifa_ifp)); + return; + } + + /* + * if I'm yet to start DAD, someone else started using this address + * first. I have a duplicate and you win. + */ + if (!dp || dp->dad_ns_ocount == 0) + duplicate++; + + /* XXX more checks for loopback situation - see nd6_dad_timer too */ + + if (duplicate) { + dp = NULL; /* will be freed in nd6_dad_duplicated() */ + nd6_dad_duplicated(ifa); + } else { + /* + * not sure if I got a duplicate. + * increment ns count and see what happens. + */ + if (dp) + dp->dad_ns_icount++; + } +} + +void +nd6_dad_na_input(ifa) + struct ifaddr *ifa; +{ + struct dadq *dp; + + if (!ifa) + panic("ifa == NULL in nd6_dad_na_input"); + + dp = nd6_dad_find(ifa); + if (dp) + dp->dad_na_icount++; + + /* remove the address. */ + nd6_dad_duplicated(ifa); +} diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c new file mode 100644 index 0000000..d177a66 --- /dev/null +++ b/sys/netinet6/nd6_rtr.c @@ -0,0 +1,1243 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define SDL(s) ((struct sockaddr_dl *)s) + +static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); +static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *)); +static struct nd_prefix *prefix_lookup __P((struct nd_prefix *)); +static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, int)); +static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, + struct nd_defrouter *)); +static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); +static void pfxrtr_del __P((struct nd_pfxrouter *)); +static void pfxlist_onlink_check __P((void)); +static void nd6_detach_prefix __P((struct nd_prefix *)); +static void nd6_attach_prefix __P((struct nd_prefix *)); + +static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, + struct in6_addrlifetime *lt6)); + +static int rt6_deleteroute __P((struct radix_node *, void *)); + +extern int nd6_recalc_reachtm_interval; + +/* + * Receive Router Solicitation Message - just for routers. + * Router solicitation/advertisement is mostly managed by userland program + * (rtadvd) so here we have no function like nd6_ra_output(). + * + * Based on RFC 2461 + */ +void +nd6_rs_input(m, off, icmp6len) + struct mbuf *m; + int off, icmp6len; +{ + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct nd_router_solicit *nd_rs + = (struct nd_router_solicit *)((caddr_t)ip6 + off); + struct in6_addr saddr6 = ip6->ip6_src; + char *lladdr = NULL; + int lladdrlen = 0; + union nd_opts ndopts; + + /* If I'm not a router, ignore it. */ + if (ip6_accept_rtadv != 0 || ip6_forwarding != 1) + return; + + /* Sanity checks */ + if (ip6->ip6_hlim != 255) { + log(LOG_ERR, + "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim); + return; + } + + /* + * Don't update the neighbor cache, if src = ::. + * This indicates that the src has no IP address assigned yet. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) + return; + + icmp6len -= sizeof(*nd_rs); + nd6_option_init(nd_rs + 1, icmp6len, &ndopts); + if (nd6_options(&ndopts) < 0) { + log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n"); + return; + } + + if (ndopts.nd_opts_src_lladdr) { + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); + lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; + } + + if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { + log(LOG_INFO, + "nd6_rs_input: lladdrlen mismatch for %s " + "(if %d, RS packet %d)\n", + ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); + } + + nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); +} + +/* + * Receive Router Advertisement Message. + * + * Based on RFC 2461 + * TODO: on-link bit on prefix information + * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing + */ +void +nd6_ra_input(m, off, icmp6len) + struct mbuf *m; + int off, icmp6len; +{ + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct nd_router_advert *nd_ra = + (struct nd_router_advert *)((caddr_t)ip6 + off); + struct in6_addr saddr6 = ip6->ip6_src; + union nd_opts ndopts; + struct nd_defrouter *dr; + + if (ip6_accept_rtadv == 0) + return; + + if (ip6->ip6_hlim != 255) { + log(LOG_ERR, + "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim); + return; + } + + if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { + log(LOG_ERR, + "nd6_ra_input: src %s is not link-local\n", + ip6_sprintf(&saddr6)); + return; + } + + icmp6len -= sizeof(*nd_ra); + nd6_option_init(nd_ra + 1, icmp6len, &ndopts); + if (nd6_options(&ndopts) < 0) { + log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n"); + return; + } + + { + struct nd_defrouter dr0; + u_int32_t advreachable = nd_ra->nd_ra_reachable; + + dr0.rtaddr = saddr6; + dr0.flags = nd_ra->nd_ra_flags_reserved; + dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); + dr0.expire = time_second + dr0.rtlifetime; + dr0.ifp = ifp; + /* unspecified or not? (RFC 2461 6.3.4) */ + if (advreachable) { + NTOHL(advreachable); + if (advreachable <= MAX_REACHABLE_TIME && + ndi->basereachable != advreachable) { + ndi->basereachable = advreachable; + ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable); + ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */ + } + } + if (nd_ra->nd_ra_retransmit) + ndi->retrans = ntohl(nd_ra->nd_ra_retransmit); + if (nd_ra->nd_ra_curhoplimit) + ndi->chlim = nd_ra->nd_ra_curhoplimit; + dr = defrtrlist_update(&dr0); + } + + /* + * prefix + */ + if (ndopts.nd_opts_pi) { + struct nd_opt_hdr *pt; + struct nd_opt_prefix_info *pi; + struct nd_prefix pr; + + for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; + pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; + pt = (struct nd_opt_hdr *)((caddr_t)pt + + (pt->nd_opt_len << 3))) { + if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) + continue; + pi = (struct nd_opt_prefix_info *)pt; + + if (pi->nd_opt_pi_len != 4) { + log(LOG_INFO, "nd6_ra_input: invalid option " + "len %d for prefix information option, " + "ignored\n", pi->nd_opt_pi_len); + continue; + } + + if (128 < pi->nd_opt_pi_prefix_len) { + log(LOG_INFO, "nd6_ra_input: invalid prefix " + "len %d for prefix information option, " + "ignored\n", pi->nd_opt_pi_prefix_len); + continue; + } + + if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) + || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { + log(LOG_INFO, "nd6_ra_input: invalid prefix " + "%s, ignored\n", + ip6_sprintf(&pi->nd_opt_pi_prefix)); + continue; + } + + /* aggregatable unicast address, rfc2374 */ + if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20 + && pi->nd_opt_pi_prefix_len != 64) { + log(LOG_INFO, "nd6_ra_input: invalid prefixlen " + "%d for rfc2374 prefix %s, ignored\n", + pi->nd_opt_pi_prefix_len, + ip6_sprintf(&pi->nd_opt_pi_prefix)); + continue; + } + + bzero(&pr, sizeof(pr)); + pr.ndpr_prefix.sin6_family = AF_INET6; + pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); + pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; + pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif; + + pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & + ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; + pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & + ND_OPT_PI_FLAG_AUTO) ? 1 : 0; + pr.ndpr_plen = pi->nd_opt_pi_prefix_len; + pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); + pr.ndpr_pltime = + ntohl(pi->nd_opt_pi_preferred_time); + + if (in6_init_prefix_ltimes(&pr)) + continue; /* prefix lifetime init failed */ + + (void)prelist_update(&pr, dr, m); + } + } + + /* + * MTU + */ + if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { + u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); + + /* lower bound */ + if (mtu < IPV6_MMTU) { + log(LOG_INFO, "nd6_ra_input: bogus mtu option " + "mtu=%d sent from %s, ignoring\n", + mtu, ip6_sprintf(&ip6->ip6_src)); + goto skip; + } + + /* upper bound */ + if (ndi->maxmtu) { + if (mtu <= ndi->maxmtu) { + int change = (ndi->linkmtu != mtu); + + ndi->linkmtu = mtu; + if (change) /* in6_maxmtu may change */ + in6_setmaxmtu(); + } else { + log(LOG_INFO, "nd6_ra_input: bogus mtu " + "mtu=%d sent from %s; " + "exceeds maxmtu %d, ignoring\n", + mtu, ip6_sprintf(&ip6->ip6_src), + ndi->maxmtu); + } + } else { + log(LOG_INFO, "nd6_ra_input: mtu option " + "mtu=%d sent from %s; maxmtu unknown, " + "ignoring\n", + mtu, ip6_sprintf(&ip6->ip6_src)); + } + } + + skip: + + /* + * Src linkaddress + */ + { + char *lladdr = NULL; + int lladdrlen = 0; + + if (ndopts.nd_opts_src_lladdr) { + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); + lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; + } + + if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { + log(LOG_INFO, + "nd6_ra_input: lladdrlen mismatch for %s " + "(if %d, RA packet %d)\n", + ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); + } + + nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); + } +} + +/* + * default router list proccessing sub routines + */ +void +defrouter_addreq(new) + struct nd_defrouter *new; +{ + struct sockaddr_in6 def, mask, gate; + int s; + + Bzero(&def, sizeof(def)); + Bzero(&mask, sizeof(mask)); + Bzero(&gate, sizeof(gate)); + + def.sin6_len = mask.sin6_len = gate.sin6_len + = sizeof(struct sockaddr_in6); + def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; + gate.sin6_addr = new->rtaddr; + + s = splnet(); + (void)rtrequest(RTM_ADD, (struct sockaddr *)&def, + (struct sockaddr *)&gate, (struct sockaddr *)&mask, + RTF_GATEWAY, NULL); + splx(s); + return; +} + +struct nd_defrouter * +defrouter_lookup(addr, ifp) + struct in6_addr *addr; + struct ifnet *ifp; +{ + struct nd_defrouter *dr; + + for(dr = nd_defrouter.lh_first; dr; dr = dr->dr_next) + if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) + return(dr); + + return(NULL); /* search failed */ +} + +void +defrouter_delreq(dr, dofree) + struct nd_defrouter *dr; + int dofree; +{ + struct sockaddr_in6 def, mask, gate; + + Bzero(&def, sizeof(def)); + Bzero(&mask, sizeof(mask)); + Bzero(&gate, sizeof(gate)); + + def.sin6_len = mask.sin6_len = gate.sin6_len + = sizeof(struct sockaddr_in6); + def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; + gate.sin6_addr = dr->rtaddr; + + rtrequest(RTM_DELETE, (struct sockaddr *)&def, + (struct sockaddr *)&gate, + (struct sockaddr *)&mask, + RTF_GATEWAY, (struct rtentry **)0); + + if (dofree) + free(dr, M_IP6NDP); + + if (nd_defrouter.lh_first) + defrouter_addreq(nd_defrouter.lh_first); + + /* + * xxx update the Destination Cache entries for all + * destinations using that neighbor as a router (7.2.5) + */ +} + +void +defrtrlist_del(dr) + struct nd_defrouter *dr; +{ + struct nd_defrouter *deldr = NULL; + struct nd_prefix *pr; + + /* + * Flush all the routing table entries that use the router + * as a next hop. + */ + if (!ip6_forwarding && ip6_accept_rtadv) { + /* above is a good condition? */ + rt6_flush(&dr->rtaddr, dr->ifp); + } + + if (dr == nd_defrouter.lh_first) + deldr = dr; /* The router is primary. */ + + LIST_REMOVE(dr, dr_entry); + + /* + * Also delete all the pointers to the router in each prefix lists. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + struct nd_pfxrouter *pfxrtr; + if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) + pfxrtr_del(pfxrtr); + } + pfxlist_onlink_check(); + + /* + * If the router is the primary one, delete the default route + * entry in the routing table. + */ + if (deldr) + defrouter_delreq(deldr, 0); + free(dr, M_IP6NDP); +} + +static struct nd_defrouter * +defrtrlist_update(new) + struct nd_defrouter *new; +{ + struct nd_defrouter *dr, *n; + int s = splnet(); + + if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) { + /* entry exists */ + if (new->rtlifetime == 0) { + defrtrlist_del(dr); + dr = NULL; + } else { + /* override */ + dr->flags = new->flags; /* xxx flag check */ + dr->rtlifetime = new->rtlifetime; + dr->expire = new->expire; + } + splx(s); + return(dr); + } + + /* entry does not exist */ + if (new->rtlifetime == 0) { + splx(s); + return(NULL); + } + + n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT); + if (n == NULL) { + splx(s); + return(NULL); + } + bzero(n, sizeof(*n)); + *n = *new; + if (nd_defrouter.lh_first == NULL) { + LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry); + defrouter_addreq(n); + } else { + LIST_INSERT_AFTER(nd_defrouter.lh_first, n, dr_entry); + defrouter_addreq(n); + } + splx(s); + + return(n); +} + +static struct nd_pfxrouter * +pfxrtr_lookup(pr, dr) + struct nd_prefix *pr; + struct nd_defrouter *dr; +{ + struct nd_pfxrouter *search; + + for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) { + if (search->router == dr) + break; + } + + return(search); +} + +static void +pfxrtr_add(pr, dr) + struct nd_prefix *pr; + struct nd_defrouter *dr; +{ + struct nd_pfxrouter *new; + + new = (struct nd_pfxrouter *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); + if (new == NULL) + return; + bzero(new, sizeof(*new)); + new->router = dr; + + LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); + + pfxlist_onlink_check(); +} + +static void +pfxrtr_del(pfr) + struct nd_pfxrouter *pfr; +{ + LIST_REMOVE(pfr, pfr_entry); + free(pfr, M_IP6NDP); +} + +static struct nd_prefix * +prefix_lookup(pr) + struct nd_prefix *pr; +{ + struct nd_prefix *search; + + for (search = nd_prefix.lh_first; search; search = search->ndpr_next) { + if (pr->ndpr_ifp == search->ndpr_ifp && + pr->ndpr_plen == search->ndpr_plen && + in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, + &search->ndpr_prefix.sin6_addr, + pr->ndpr_plen) + ) { + break; + } + } + + return(search); +} + +static int +prelist_add(pr, dr) + struct nd_prefix *pr; + struct nd_defrouter *dr; +{ + struct nd_prefix *new; + int i, s; + + new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); + if (new == NULL) + return ENOMEM; + bzero(new, sizeof(*new)); + *new = *pr; + + /* initilization */ + new->ndpr_statef_onlink = pr->ndpr_statef_onlink; + LIST_INIT(&new->ndpr_advrtrs); + in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); + /* make prefix in the canonical form */ + for (i = 0; i < 4; i++) + new->ndpr_prefix.sin6_addr.s6_addr32[i] &= + new->ndpr_mask.s6_addr32[i]; + + /* xxx ND_OPT_PI_FLAG_ONLINK processing */ + + s = splnet(); + /* link ndpr_entry to nd_prefix list */ + LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); + splx(s); + + if (dr) + pfxrtr_add(new, dr); + + return 0; +} + +void +prelist_remove(pr) + struct nd_prefix *pr; +{ + struct nd_pfxrouter *pfr, *next; + int s; + + s = splnet(); + /* unlink ndpr_entry from nd_prefix list */ + LIST_REMOVE(pr, ndpr_entry); + splx(s); + + /* free list of routers that adversed the prefix */ + for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { + next = pfr->pfr_next; + + free(pfr, M_IP6NDP); + } + free(pr, M_IP6NDP); + + pfxlist_onlink_check(); +} + +/* + * NOTE: We set address lifetime to keep + * address lifetime <= prefix lifetime + * invariant. This is to simplify on-link determination code. + * If onlink determination is udated, this routine may have to be updated too. + */ +int +prelist_update(new, dr, m) + struct nd_prefix *new; + struct nd_defrouter *dr; /* may be NULL */ + struct mbuf *m; +{ + struct in6_ifaddr *ia6 = NULL; + struct nd_prefix *pr; + int s = splnet(); + int error = 0; + int auth; + struct in6_addrlifetime *lt6; + + auth = 0; + if (m) { + /* + * Authenticity for NA consists authentication for + * both IP header and IP datagrams, doesn't it ? + */ +#if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM) + auth = (m->m_flags & M_AUTHIPHDR + && m->m_flags & M_AUTHIPDGM) ? 1 : 0; +#endif + } + + if ((pr = prefix_lookup(new)) != NULL) { + if (pr->ndpr_ifp != new->ndpr_ifp) { + error = EADDRNOTAVAIL; + goto end; + } + /* update prefix information */ + pr->ndpr_flags = new->ndpr_flags; + pr->ndpr_vltime = new->ndpr_vltime; + pr->ndpr_pltime = new->ndpr_pltime; + pr->ndpr_preferred = new->ndpr_preferred; + pr->ndpr_expire = new->ndpr_expire; + + /* + * RFC 2462 5.5.3 (d) or (e) + * We got a prefix which we have seen in the past. + */ + if (!new->ndpr_raf_auto) + goto noautoconf1; + + if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + ia6 = NULL; + else + ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + + if (ia6 == NULL) { + /* + * Special case: + * (1) We have seen the prefix advertised before, but + * we have never performed autoconfig for this prefix. + * This is because Autonomous bit was 0 previously, or + * autoconfig failed due to some other reasons. + * (2) We have seen the prefix advertised before and + * we have performed autoconfig in the past, but + * we seem to have no interface address right now. + * This is because the interface address have expired. + * + * This prefix is fresh, with respect to autoconfig + * process. + * + * Add an address based on RFC 2462 5.5.3 (d). + */ + ia6 = in6_ifadd(pr->ndpr_ifp, + &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr, + new->ndpr_plen); + if (!ia6) { + error = EADDRNOTAVAIL; + log(LOG_ERR, "prelist_update: failed to add a " + "new address\n"); + goto noautoconf1; + } + + lt6 = &ia6->ia6_lifetime; + + /* address lifetime <= prefix lifetime */ + lt6->ia6t_vltime = new->ndpr_vltime; + lt6->ia6t_pltime = new->ndpr_pltime; + in6_init_address_ltimes(new, lt6); + } else { +#define TWOHOUR (120*60) + /* + * We have seen the prefix before, and we have added + * interface address in the past. We still have + * the interface address assigned. + * + * update address lifetime based on RFC 2462 + * 5.5.3 (e). + */ + int update = 0; + + lt6 = &ia6->ia6_lifetime; + + /* + * update to RFC 2462 5.5.3 (e) from Jim Bound, + * (ipng 6712) + */ + if (TWOHOUR < new->ndpr_vltime + || lt6->ia6t_vltime < new->ndpr_vltime) { + lt6->ia6t_vltime = new->ndpr_vltime; + update++; + } else if (auth) { + lt6->ia6t_vltime = new->ndpr_vltime; + update++; + } + + /* jim bound rule is not imposed for pref lifetime */ + lt6->ia6t_pltime = new->ndpr_pltime; + + update++; + if (update) + in6_init_address_ltimes(new, lt6); + } + + noautoconf1: + + if (dr && pfxrtr_lookup(pr, dr) == NULL) + pfxrtr_add(pr, dr); + } else { + int error_tmp; + + if (new->ndpr_vltime == 0) goto end; + + bzero(&new->ndpr_addr, sizeof(struct in6_addr)); + + /* + * RFC 2462 5.5.3 (d) + * We got a fresh prefix. Perform some sanity checks + * and add an interface address by appending interface ID + * to the advertised prefix. + */ + if (!new->ndpr_raf_auto) + goto noautoconf2; + + ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr, + &new->ndpr_addr, new->ndpr_plen); + if (!ia6) { + error = EADDRNOTAVAIL; + log(LOG_ERR, "prelist_update: " + "failed to add a new address\n"); + goto noautoconf2; + } + /* set onlink bit if an interface route is configured */ + new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0; + + lt6 = &ia6->ia6_lifetime; + + /* address lifetime <= prefix lifetime */ + lt6->ia6t_vltime = new->ndpr_vltime; + lt6->ia6t_pltime = new->ndpr_pltime; + in6_init_address_ltimes(new, lt6); + + noautoconf2: + error_tmp = prelist_add(new, dr); + error = error_tmp ? error_tmp : error; + } + + end: + splx(s); + return error; +} + +/* + * Check if each prefix in the prefix list has at least one available router + * that advertised the prefix. + * If the check fails, the prefix may be off-link because, for example, + * we have moved from the network but the lifetime of the prefix has not + * been expired yet. So we should not use the prefix if there is another + * prefix that has an available router. + * But if there is no prefix that has an availble router, we still regards + * all the prefixes as on-link. This is because we can't tell if all the + * routers are simply dead or if we really moved from the network and there + * is no router around us. + */ +static void +pfxlist_onlink_check() +{ + struct nd_prefix *pr; + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) + if (pr->ndpr_advrtrs.lh_first) /* pr has an available router */ + break; + + if (pr) { + /* + * There is at least one prefix that has a router. First, + * detach prefixes which has no advertising router and then + * attach other prefixes. The order is important since an + * attached prefix and a detached prefix may have a same + * interface route. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (pr->ndpr_advrtrs.lh_first == NULL && + pr->ndpr_statef_onlink) { + pr->ndpr_statef_onlink = 0; + nd6_detach_prefix(pr); + } + } + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (pr->ndpr_advrtrs.lh_first && + pr->ndpr_statef_onlink == 0) + nd6_attach_prefix(pr); + } + } else { + /* there is no prefix that has a router */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) + if (pr->ndpr_statef_onlink == 0) + nd6_attach_prefix(pr); + } +} + +static void +nd6_detach_prefix(pr) + struct nd_prefix *pr; +{ + struct in6_ifaddr *ia6; + struct sockaddr_in6 sa6, mask6; + + /* + * Delete the interface route associated with the prefix. + */ + bzero(&sa6, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_len = sizeof(sa6); + bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, + sizeof(struct in6_addr)); + bzero(&mask6, sizeof(mask6)); + mask6.sin6_family = AF_INET6; + mask6.sin6_len = sizeof(sa6); + bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); + { + int e; + + e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, + (struct sockaddr *)&mask6, 0, NULL); + if (e) { + log(LOG_ERR, + "nd6_detach_prefix: failed to delete route: " + "%s/%d (errno = %d)\n", + ip6_sprintf(&sa6.sin6_addr), + pr->ndpr_plen, + e); + } + } + + /* + * Mark the address derived from the prefix detached so that + * it won't be used as a source address for a new connection. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + ia6 = NULL; + else + ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + if (ia6) + ia6->ia6_flags |= IN6_IFF_DETACHED; +} + +static void +nd6_attach_prefix(pr) + struct nd_prefix *pr; +{ + struct ifaddr *ifa; + struct in6_ifaddr *ia6; + + /* + * Add the interface route associated with the prefix(if necessary) + * Should we consider if the L bit is set in pr->ndpr_flags? + */ + ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix, + pr->ndpr_ifp); + if (ifa == NULL) { + log(LOG_ERR, + "nd6_attach_prefix: failed to find any ifaddr" + " to add route for a prefix(%s/%d)\n", + ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen); + } else { + int e; + struct sockaddr_in6 mask6; + + bzero(&mask6, sizeof(mask6)); + mask6.sin6_family = AF_INET6; + mask6.sin6_len = sizeof(mask6); + mask6.sin6_addr = pr->ndpr_mask; + e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, + ifa->ifa_addr, (struct sockaddr *)&mask6, + ifa->ifa_flags, NULL); + if (e == 0) + pr->ndpr_statef_onlink = 1; + else { + log(LOG_ERR, + "nd6_attach_prefix: failed to add route for" + " a prefix(%s/%d), errno = %d\n", + ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e); + } + } + + /* + * Now the address derived from the prefix can be used as a source + * for a new connection, so clear the detached flag. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + ia6 = NULL; + else + ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + if (ia6) { + ia6->ia6_flags &= ~IN6_IFF_DETACHED; + if (pr->ndpr_statef_onlink) + ia6->ia_flags |= IFA_ROUTE; + } +} + +static struct in6_ifaddr * +in6_ifadd(ifp, in6, addr, prefixlen) + struct ifnet *ifp; + struct in6_addr *in6; + struct in6_addr *addr; + int prefixlen; /* prefix len of the new prefix in "in6" */ +{ + struct ifaddr *ifa; + struct in6_ifaddr *ia, *ib, *oia; + int s, error; + struct in6_addr mask; + + in6_len2mask(&mask, prefixlen); + + /* find link-local address (will be interface ID) */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + if (ifa) + ib = (struct in6_ifaddr *)ifa; + else + return NULL; + + /* prefixlen + ifidlen must be equal to 128 */ + if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) { + log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s" + "(prefix=%d ifid=%d)\n", if_name(ifp), + prefixlen, + 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr)); + return NULL; + } + + /* make ifaddr */ + ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT); + if (ia == NULL) { + printf("ENOBUFS in in6_ifadd %d\n", __LINE__); + return NULL; + } + + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; + ia->ia_ifp = ifp; + + /* link to in6_ifaddr */ + if ((oia = in6_ifaddr) != NULL) { + for( ; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; + + /* link to if_addrlist */ + if (ifp->if_addrlist.tqh_first != NULL) { + TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, + ifa_list); + } + + /* new address */ + ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_addr.sin6_family = AF_INET6; + /* prefix */ + bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)); + ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; + ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; + ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; + ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; + /* interface ID */ + ia->ia_addr.sin6_addr.s6_addr32[0] + |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); + ia->ia_addr.sin6_addr.s6_addr32[1] + |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); + ia->ia_addr.sin6_addr.s6_addr32[2] + |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); + ia->ia_addr.sin6_addr.s6_addr32[3] + |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); + + /* new prefix */ + ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_prefixmask.sin6_family = AF_INET6; + bcopy(&mask, &ia->ia_prefixmask.sin6_addr, + sizeof(ia->ia_prefixmask.sin6_addr)); + + /* same routine */ + ia->ia_ifa.ifa_rtrequest = + (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + ia->ia_ifa.ifa_metric = ifp->if_metric; + + /* add interface route */ + if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) { + log(LOG_NOTICE, "in6_ifadd: failed to add an interface route " + "for %s/%d on %s, errno = %d\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen, + if_name(ifp), error); + } else + ia->ia_flags |= IFA_ROUTE; + + *addr = ia->ia_addr.sin6_addr; + + if (ifp->if_flags & IFF_MULTICAST) { + int error; /* not used */ + struct in6_addr sol6; + + /* join solicited node multicast address */ + bzero(&sol6, sizeof(sol6)); + sol6.s6_addr16[0] = htons(0xff02); + sol6.s6_addr16[1] = htons(ifp->if_index); + sol6.s6_addr32[1] = 0; + sol6.s6_addr32[2] = htonl(1); + sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; + sol6.s6_addr8[12] = 0xff; + (void)in6_addmulti(&sol6, ifp, &error); + } + + ia->ia6_flags |= IN6_IFF_TENTATIVE; + + /* + * To make the interface up. Only AF_INET6 in ia is used... + */ + s = splimp(); + if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) { + splx(s); + return NULL; + } + splx(s); + + /* Perform DAD, if needed. */ + nd6_dad_start((struct ifaddr *)ia, NULL); + + return ia; +} + +int +in6_ifdel(ifp, in6) + struct ifnet *ifp; + struct in6_addr *in6; +{ + struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL; + struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL; + + if (!ifp) + return -1; + + ia = in6ifa_ifpwithaddr(ifp, in6); + if (!ia) + return -1; + + if (ifp->if_flags & IFF_MULTICAST) { + /* + * delete solicited multicast addr for deleting host id + */ + struct in6_multi *in6m; + struct in6_addr llsol; + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = + ia->ia_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } + + if (ia->ia_flags & IFA_ROUTE) { + rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); + ia->ia_flags &= ~IFA_ROUTE; + } + + TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + + /* lladdr is never deleted */ + oia = ia; + if (oia == (ia = in6_ifaddr)) + in6_ifaddr = ia->ia_next; + else { + while (ia->ia_next && (ia->ia_next != oia)) + ia = ia->ia_next; + if (ia->ia_next) + ia->ia_next = oia->ia_next; + else + return -1; + } + + IFAFREE((&oia->ia_ifa)); +/* xxx + rtrequest(RTM_DELETE, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)0 + (struct sockaddr *)&ia->ia_prefixmask, + RTF_UP|RTF_CLONING, + (struct rtentry **)0); +*/ + return 0; +} + +int +in6_init_prefix_ltimes(struct nd_prefix *ndpr) +{ + + /* check if preferred lifetime > valid lifetime */ + if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { + log(LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" + "(%d) is greater than valid lifetime(%d)\n", + (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime); + return (EINVAL); + } + if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) + ndpr->ndpr_preferred = 0; + else + ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime; + if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) + ndpr->ndpr_expire = 0; + else + ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime; + + return 0; +} + +static void +in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) +{ + + /* init ia6t_expire */ + if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) + lt6->ia6t_expire = 0; + else { + lt6->ia6t_expire = time_second; + lt6->ia6t_expire += lt6->ia6t_vltime; + } + /* init ia6t_preferred */ + if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) + lt6->ia6t_preferred = 0; + else { + lt6->ia6t_preferred = time_second; + lt6->ia6t_preferred += lt6->ia6t_pltime; + } + /* ensure addr lifetime <= prefix lifetime */ + if (new->ndpr_expire && lt6->ia6t_expire && + new->ndpr_expire < lt6->ia6t_expire) + lt6->ia6t_expire = new->ndpr_expire; + if (new->ndpr_preferred && lt6->ia6t_preferred + && new->ndpr_preferred < lt6->ia6t_preferred) + lt6->ia6t_preferred = new->ndpr_preferred; +} + +/* + * Delete all the routing table entries that use the specified gateway. + * XXX: this function causes search through all entries of routing table, so + * it shouldn't be called when acting as a router. + */ +void +rt6_flush(gateway, ifp) + struct in6_addr *gateway; + struct ifnet *ifp; +{ + struct radix_node_head *rnh = rt_tables[AF_INET6]; + int s = splnet(); + + /* We'll care only link-local addresses */ + if (!IN6_IS_ADDR_LINKLOCAL(gateway)) { + splx(s); + return; + } + /* XXX: hack for KAME's link-local address kludge */ + gateway->s6_addr16[1] = htons(ifp->if_index); + + rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); + splx(s); +} + +static int +rt6_deleteroute(rn, arg) + struct radix_node *rn; + void *arg; +{ +#define SIN6(s) ((struct sockaddr_in6 *)s) + struct rtentry *rt = (struct rtentry *)rn; + struct in6_addr *gate = (struct in6_addr *)arg; + + if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) + return(0); + + if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr)) + return(0); + + /* + * We delete only host route. This means, in particular, we don't + * delete default route. + */ + if ((rt->rt_flags & RTF_HOST) == 0) + return(0); + + return(rtrequest(RTM_DELETE, rt_key(rt), + rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0)); +#undef SIN6 +} diff --git a/sys/netinet6/pim6.h b/sys/netinet6/pim6.h new file mode 100644 index 0000000..1e47785 --- /dev/null +++ b/sys/netinet6/pim6.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Protocol Independent Multicast (PIM) definitions + * + * Written by Ahmed Helmy, SGI, July 1996 + * + * MULTICAST + */ + +/* + * PIM packet header + */ +#define PIM_VERSION 2 +struct pim { +#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) + u_char pim_type:4, /* the PIM message type, currently they are: + * Hello, Register, Register-Stop, Join/Prune, + * Bootstrap, Assert, Graft (PIM-DM only), + * Graft-Ack (PIM-DM only), C-RP-Adv + */ + pim_ver:4; /* PIM version number; 2 for PIMv2 */ +#else + u_char pim_ver:4, /* PIM version */ + pim_type:4; /* PIM type */ +#endif + u_char pim_rsv; /* Reserved */ + u_short pim_cksum; /* IP style check sum */ +}; + +#define PIM_MINLEN 8 /* The header min. length is 8 */ +#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */ + +/* + * Message types + */ +#define PIM_REGISTER 1 /* PIM Register type is 1 */ + +/* second bit in reg_head is the null bit */ +#define PIM_NULL_REGISTER 0x40000000 diff --git a/sys/netinet6/pim6_var.h b/sys/netinet6/pim6_var.h new file mode 100644 index 0000000..3610a40 --- /dev/null +++ b/sys/netinet6/pim6_var.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* $Id: pim6_var.h,v 1.2 1999/08/01 15:58:13 itojun Exp $ */ + +#ifndef _NETINET6_PIM6_VAR_H_ +#define _NETINET6_PIM6_VAR_H_ + +/* + * Protocol Independent Multicast (PIM), + * implementation-specific definitions. + * + * Written by George Edmond Eddy (Rusty), ISI, February 1998 + * Modified by Pavlin Ivanov Radoslavov, USC/ISI, May 1998 + */ + +struct pim6stat { + u_int pim6s_rcv_total; /* total PIM messages received */ + u_int pim6s_rcv_tooshort; /* received with too few bytes */ + u_int pim6s_rcv_badsum; /* received with bad checksum */ + u_int pim6s_rcv_badversion; /* received bad PIM version */ + u_int pim6s_rcv_registers; /* received registers */ + u_int pim6s_rcv_badregisters; /* received invalid registers */ + u_int pim6s_snd_registers; /* sent registers */ +}; + +#if (defined(KERNEL)) || (defined(_KERNEL)) +extern struct pim6stat pim6stat; + +int pim6_input __P((struct mbuf **, int*, int)); +#endif /* KERNEL */ + +/* + * Names for PIM sysctl objects + */ +#define PIMCTL_STATS 1 /* statistics (read-only) */ +#define PIMCTL_MAXID 2 + +#define PIMCTL_NAMES { \ + { 0, 0 }, \ + { 0, 0 }, \ +} + +#endif /* _NETINET6_PIM6_VAR_H_ */ diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c new file mode 100644 index 0000000..5698f1c3 --- /dev/null +++ b/sys/netinet6/raw_ip6.c @@ -0,0 +1,612 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 + */ + +#include "opt_inet.h" +#include "opt_ipsec.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef IPSEC +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#endif /*IPSEC*/ + +#include + +/* #include "faith.h" */ + +#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) +#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) + +/* + * Raw interface to IP6 protocol. + */ + +extern struct inpcbhead ripcb; +extern struct inpcbinfo ripcbinfo; +extern u_long rip_sendspace; +extern u_long rip_recvspace; + +/* + * Setup generic address and protocol structures + * for raw_input routine, then pass them along with + * mbuf chain. + */ +int +rip6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + struct mbuf *m = *mp; + register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + register struct inpcb *in6p; + struct inpcb *last = 0; + struct mbuf *opts = 0; + struct sockaddr_in6 rip6src; + +#if defined(NFAITH) && 0 < NFAITH + if (m->m_pkthdr.rcvif) { + if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { + /* XXX send icmp6 host/port unreach? */ + m_freem(m); + return IPPROTO_DONE; + } + } +#endif + init_sin6(&rip6src, m); /* general init */ + + LIST_FOREACH(in6p, &ripcb, inp_list) { + if ((in6p->in6p_vflag & INP_IPV6) == NULL) + continue; + if (in6p->in6p_ip6_nxt && + in6p->in6p_ip6_nxt != proto) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && + !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) + continue; + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && + !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) + continue; + if (in6p->in6p_cksum != -1 + && in6_cksum(m, ip6->ip6_nxt, *offp, + m->m_pkthdr.len - *offp)) { + /* XXX bark something */ + continue; + } + if (last) { + struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); + if (n) { + if (last->in6p_flags & IN6P_CONTROLOPTS || + last->in6p_socket->so_options & SO_TIMESTAMP) + ip6_savecontrol(last, &opts, ip6, n); + /* strip intermediate headers */ + m_adj(n, *offp); + if (sbappendaddr(&last->in6p_socket->so_rcv, + (struct sockaddr *)&rip6src, + n, opts) == 0) { + /* should notify about lost packet */ + m_freem(n); + if (opts) + m_freem(opts); + } else + sorwakeup(last->in6p_socket); + opts = NULL; + } + } + last = in6p; + } + if (last) { + if (last->in6p_flags & IN6P_CONTROLOPTS || + last->in6p_socket->so_options & SO_TIMESTAMP) + ip6_savecontrol(last, &opts, ip6, m); + /* strip intermediate headers */ + m_adj(m, *offp); + if (sbappendaddr(&last->in6p_socket->so_rcv, + (struct sockaddr *)&rip6src, m, opts) == 0) { + m_freem(m); + if (opts) + m_freem(opts); + } else + sorwakeup(last->in6p_socket); + } else { + if (proto == IPPROTO_NONE) + m_freem(m); + else { + char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_NEXTHEADER, + prvnxtp - mtod(m, char *)); + } + ip6stat.ip6s_delivered--; + } + return IPPROTO_DONE; +} + +/* + * Generate IPv6 header and pass packet to ip6_output. + * Tack on options user may have setup with control call. + */ +int +#if __STDC__ +rip6_output(struct mbuf *m, ...) +#else +rip6_output(m, va_alist) + struct mbuf *m; + va_dcl +#endif +{ + struct socket *so; + struct sockaddr_in6 *dstsock; + struct mbuf *control; + struct in6_addr *dst; + struct ip6_hdr *ip6; + struct inpcb *in6p; + u_int plen = m->m_pkthdr.len; + int error = 0; + struct ip6_pktopts opt, *optp = 0; + struct ifnet *oifp = NULL; + int type = 0, code = 0; /* for ICMPv6 output statistics only */ + int priv = 0; + va_list ap; + + va_start(ap, m); + so = va_arg(ap, struct socket *); + dstsock = va_arg(ap, struct sockaddr_in6 *); + control = va_arg(ap, struct mbuf *); + va_end(ap); + + in6p = sotoin6pcb(so); + + priv = 0; + if (so->so_cred->cr_uid == 0) + priv = 1; + dst = &dstsock->sin6_addr; + if (control) { + if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + goto bad; + optp = &opt; + } else + optp = in6p->in6p_outputopts; + + /* + * For an ICMPv6 packet, we should know its type and code + * to update statistics. + */ + if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + struct icmp6_hdr *icmp6; + if (m->m_len < sizeof(struct icmp6_hdr) && + (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { + error = ENOBUFS; + goto bad; + } + icmp6 = mtod(m, struct icmp6_hdr *); + type = icmp6->icmp6_type; + code = icmp6->icmp6_code; + } + + M_PREPEND(m, sizeof(*ip6), M_WAIT); + ip6 = mtod(m, struct ip6_hdr *); + + /* + * Next header might not be ICMP6 but use its pseudo header anyway. + */ + ip6->ip6_dst = *dst; + + /* + * If the scope of the destination is link-local, embed the interface + * index in the address. + * + * XXX advanced-api value overrides sin6_scope_id + */ + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + struct in6_pktinfo *pi; + + /* + * XXX Boundary check is assumed to be already done in + * ip6_setpktoptions(). + */ + if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) { + ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex); + oifp = ifindex2ifnet[pi->ipi6_ifindex]; + } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && + in6p->in6p_moptions && + in6p->in6p_moptions->im6o_multicast_ifp) { + oifp = in6p->in6p_moptions->im6o_multicast_ifp; + ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index); + } else if (dstsock->sin6_scope_id) { + /* boundary check */ + if (dstsock->sin6_scope_id < 0 + || if_index < dstsock->sin6_scope_id) { + error = ENXIO; /* XXX EINVAL? */ + goto bad; + } + ip6->ip6_dst.s6_addr16[1] + = htons(dstsock->sin6_scope_id & 0xffff);/*XXX*/ + } + } + + /* + * Source address selection. + */ + { + struct in6_addr *in6a; + + if ((in6a = in6_selectsrc(dstsock, optp, + in6p->in6p_moptions, + &in6p->in6p_route, + &in6p->in6p_laddr, + &error)) == 0) { + if (error == 0) + error = EADDRNOTAVAIL; + goto bad; + } + ip6->ip6_src = *in6a; + if (in6p->in6p_route.ro_rt) + oifp = ifindex2ifnet[in6p->in6p_route.ro_rt->rt_ifp->if_index]; + } + + ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; + ip6->ip6_vfc = IPV6_VERSION; + /* ip6_plen will be filled in ip6_output, so not fill it here. */ + ip6->ip6_nxt = in6p->in6p_ip6_nxt; + ip6->ip6_hlim = in6_selecthlim(in6p, oifp); + + if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || + in6p->in6p_cksum != -1) { + struct mbuf *n; + int off; + u_int16_t *p; + +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) /* XXX */ + + /* compute checksum */ + if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) + off = offsetof(struct icmp6_hdr, icmp6_cksum); + else + off = in6p->in6p_cksum; + if (plen < off + 1) { + error = EINVAL; + goto bad; + } + off += sizeof(struct ip6_hdr); + + n = m; + while (n && n->m_len <= off) { + off -= n->m_len; + n = n->m_next; + } + if (!n) + goto bad; + p = (u_int16_t *)(mtod(n, caddr_t) + off); + *p = 0; + *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); + } + +#ifdef IPSEC + m->m_pkthdr.rcvif = (struct ifnet *)so; +#endif /*IPSEC*/ + + error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions, + &oifp); + if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + if (oifp) + icmp6_ifoutstat_inc(oifp, type, code); + icmp6stat.icp6s_outhist[type]++; + } + + goto freectl; + + bad: + if (m) + m_freem(m); + + freectl: + if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt) + RTFREE(optp->ip6po_route.ro_rt); + if (control) + m_freem(control); + return(error); +} + +/* + * Raw IPv6 socket option processing. + */ +int +rip6_ctloutput(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + int error; + + if (sopt->sopt_level == IPPROTO_ICMPV6) + /* + * XXX: is it better to call icmp6_ctloutput() directly + * from protosw? + */ + return(icmp6_ctloutput(so, sopt)); + else if (sopt->sopt_level != IPPROTO_IPV6) + return (EINVAL); + + error = 0; + + switch (sopt->sopt_dir) { + case SOPT_GET: + switch (sopt->sopt_name) { + default: + error = ip6_ctloutput(so, sopt); + break; + } + break; + + case SOPT_SET: + switch (sopt->sopt_name) { + default: + error = ip6_ctloutput(so, sopt); + break; + } + break; + } + + return (error); +} + +static int +rip6_attach(struct socket *so, int proto, struct proc *p) +{ + struct inpcb *inp; + int error, s; + + inp = sotoinpcb(so); + if (inp) + panic("rip6_attach"); + if (p && (error = suser(p)) != 0) + return error; + + if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { + error = soreserve(so, rip_sendspace, rip_recvspace); + if (error) + return error; + } + s = splnet(); + error = in_pcballoc(so, &ripcbinfo, p); + splx(s); + if (error) + return error; + inp = (struct inpcb *)so->so_pcb; + inp->inp_vflag |= INP_IPV6; + inp->in6p_ip6_nxt = (long)proto; + inp->in6p_hops = -1; /* use kernel default */ + inp->in6p_cksum = -1; +#ifdef IPSEC + error = ipsec_init_policy(so, &inp->in6p_sp); + if (error != 0) { + in6_pcbdetach(inp); + return (error); + } +#endif /*IPSEC*/ + MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *, + sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); + ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); + return 0; +} + +static int +rip6_detach(struct socket *so) +{ + struct inpcb *inp; + + inp = sotoinpcb(so); + if (inp == 0) + panic("rip6_detach"); + /* xxx: RSVP */ + if (inp->in6p_icmp6filt) { + FREE(inp->in6p_icmp6filt, M_PCB); + inp->in6p_icmp6filt = NULL; + } + in6_pcbdetach(inp); + return 0; +} + +static int +rip6_abort(struct socket *so) +{ + soisdisconnected(so); + return rip6_detach(so); +} + +static int +rip6_disconnect(struct socket *so) +{ + struct inpcb *inp = sotoinpcb(so); + + if ((so->so_state & SS_ISCONNECTED) == 0) + return ENOTCONN; + inp->in6p_faddr = in6addr_any; + return rip6_abort(so); +} + +static int +rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) +{ + struct inpcb *inp = sotoinpcb(so); + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; + struct ifaddr *ia = NULL; + + if (nam->sa_len != sizeof(*addr)) + return EINVAL; + + if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) + return EADDRNOTAVAIL; + if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && + (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) + return EADDRNOTAVAIL; + if (ia && + ((struct in6_ifaddr *)ia)->ia6_flags & + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| + IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + return(EADDRNOTAVAIL); + } + inp->in6p_laddr = addr->sin6_addr; + return 0; +} + +static int +rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +{ + struct inpcb *inp = sotoinpcb(so); + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; + struct in6_addr *in6a = NULL; + int error = 0; + + if (nam->sa_len != sizeof(*addr)) + return EINVAL; + if (TAILQ_EMPTY(&ifnet)) + return EADDRNOTAVAIL; + if (addr->sin6_family != AF_INET6) + return EAFNOSUPPORT; + + /* Source address selection. XXX: need pcblookup? */ + in6a = in6_selectsrc(addr, inp->in6p_outputopts, + inp->in6p_moptions, &inp->in6p_route, + &inp->in6p_laddr, &error); + if (in6a == NULL) + return (error ? error : EADDRNOTAVAIL); + inp->in6p_laddr = *in6a; + inp->in6p_faddr = addr->sin6_addr; + soisconnected(so); + return 0; +} + +static int +rip6_shutdown(struct socket *so) +{ + socantsendmore(so); + return 0; +} + +static int +rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, + struct mbuf *control, struct proc *p) +{ + struct inpcb *inp = sotoinpcb(so); + struct sockaddr_in6 tmp; + struct sockaddr_in6 *dst; + + if (so->so_state & SS_ISCONNECTED) { + if (nam) { + m_freem(m); + return EISCONN; + } + /* XXX */ + bzero(&tmp, sizeof(tmp)); + tmp.sin6_family = AF_INET6; + tmp.sin6_len = sizeof(struct sockaddr_in6); + bcopy(&inp->in6p_faddr, &tmp.sin6_addr, + sizeof(struct in6_addr)); + dst = &tmp; + } else { + if (nam == NULL) { + m_freem(m); + return ENOTCONN; + } + dst = (struct sockaddr_in6 *)nam; + } + return rip6_output(m, so, dst, control); +} + +struct pr_usrreqs rip6_usrreqs = { + rip6_abort, pru_accept_notsupp, rip6_attach, rip6_bind, rip6_connect, + pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect, + pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp, + pru_rcvoob_notsupp, rip6_send, pru_sense_null, rip6_shutdown, + in6_setsockaddr, sosend, soreceive, sopoll +}; diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c new file mode 100644 index 0000000..d50cf90 --- /dev/null +++ b/sys/netinet6/route6.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *, struct ip6_rthdr0 *)); + +int +route6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; /* proto is unused */ +{ + register struct ip6_hdr *ip6; + register struct mbuf *m = *mp; + register struct ip6_rthdr *rh; + int off = *offp, rhlen; + + IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE); + ip6 = mtod(m, struct ip6_hdr *); + rh = (struct ip6_rthdr *)((caddr_t)ip6 + off); + + switch(rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rhlen = (rh->ip6r_len + 1) << 3; + IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE); + if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) + return(IPPROTO_DONE); + break; + default: + /* unknown routing type */ + if (rh->ip6r_segleft == 0) { + rhlen = (rh->ip6r_len + 1) << 3; + break; /* Final dst. Just ignore the header. */ + } + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, + (caddr_t)&rh->ip6r_type - (caddr_t)ip6); + return(IPPROTO_DONE); + } + + *offp += rhlen; + return(rh->ip6r_nxt); +} + +/* + * Type0 routing header processing + */ +static int +ip6_rthdr0(m, ip6, rh0) + struct mbuf *m; + struct ip6_hdr *ip6; + struct ip6_rthdr0 *rh0; +{ + int addrs, index; + struct in6_addr *nextaddr, tmpaddr; + + if (rh0->ip6r0_segleft == 0) + return(0); + + if (rh0->ip6r0_len % 2 +#ifdef COMPAT_RFC1883 + || rh0->ip6r0_len > 46 +#endif + ) { + /* + * Type 0 routing header can't contain more than 23 addresses. + * RFC 2462: this limitation was removed since stict/loose + * bitmap field was deleted. + */ + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, + (caddr_t)&rh0->ip6r0_len - (caddr_t)ip6); + return(-1); + } + + if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, + (caddr_t)&rh0->ip6r0_segleft - (caddr_t)ip6); + return(-1); + } + + index = addrs - rh0->ip6r0_segleft; + rh0->ip6r0_segleft--; + nextaddr = rh0->ip6r0_addr + index; + + if (IN6_IS_ADDR_MULTICAST(nextaddr) || + IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + ip6stat.ip6s_badoptions++; + m_freem(m); + return(-1); + } + + /* + * Swap the IPv6 destination address and nextaddr. Forward the packet. + */ + tmpaddr = *nextaddr; + *nextaddr = ip6->ip6_dst; + if (IN6_IS_ADDR_LINKLOCAL(nextaddr)) + nextaddr->s6_addr16[1] = 0; + ip6->ip6_dst = tmpaddr; + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + +#ifdef COMPAT_RFC1883 + if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8)))) + ip6_forward(m, IPV6_SRCRT_NEIGHBOR); + else + ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR); +#else + ip6_forward(m, 1); +#endif + + return(-1); /* m would be freed in ip6_forward() */ +} diff --git a/sys/netinet6/tcp6_var.h b/sys/netinet6/tcp6_var.h new file mode 100644 index 0000000..93f41d9 --- /dev/null +++ b/sys/netinet6/tcp6_var.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1993, 1994, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp_var.h 8.4 (Berkeley) 5/24/95 + * $FreeBSD$ + */ + +#ifndef _NETINET_TCP6_VAR_H_ +#define _NETINET_TCP6_VAR_H_ + +#ifdef KERNEL + +struct ip6_hdr; +void tcp6_ctlinput __P((int, struct sockaddr *, void *)); +void tcp6_init __P((void)); +int tcp6_input __P((struct mbuf **, int *, int)); +struct rtentry *tcp_rtlookup6 __P((struct inpcb *)); + +extern struct pr_usrreqs tcp6_usrreqs; + +#endif /* KERNEL */ + +#endif /* _NETINET_TCP6_VAR_H_ */ diff --git a/sys/netinet6/udp6_var.h b/sys/netinet6/udp6_var.h new file mode 100644 index 0000000..b95bc78 --- /dev/null +++ b/sys/netinet6/udp6_var.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET6_UDP6_VAR_H_ +#define _NETINET6_UDP6_VAR_H_ + +#ifdef KERNEL +extern struct pr_usrreqs udp6_usrreqs; + +void udp6_ctlinput __P((int, struct sockaddr *, void *)); +int udp6_input __P((struct mbuf **, int *, int)); +int udp6_output __P((struct inpcb *inp, struct mbuf *m, + struct sockaddr *addr, struct mbuf *control, + struct proc *p)); +#endif /* KERNEL */ + +#endif /*_NETINET6_UDP6_VAR_H_*/ diff --git a/sys/netkey/key_var.h b/sys/netkey/key_var.h new file mode 100644 index 0000000..41750b7 --- /dev/null +++ b/sys/netkey/key_var.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETKEY_KEY_VAR_H_ +#define _NETKEY_KEY_VAR_H_ + +/* sysctl */ +#define KEYCTL_DEBUG_LEVEL 1 +#define KEYCTL_SPI_TRY 2 +#define KEYCTL_SPI_MIN_VALUE 3 +#define KEYCTL_SPI_MAX_VALUE 4 +#define KEYCTL_RANDOM_INT 5 +#define KEYCTL_LARVAL_LIFETIME 6 +#define KEYCTL_BLOCKACQ_COUNT 7 +#define KEYCTL_BLOCKACQ_LIFETIME 8 +#define KEYCTL_MAXID 9 + +#define KEYCTL_NAMES { \ + { 0, 0 }, \ + { "debug", CTLTYPE_INT }, \ + { "spi_try", CTLTYPE_INT }, \ + { "spi_min_value", CTLTYPE_INT }, \ + { "spi_max_value", CTLTYPE_INT }, \ + { "random_int", CTLTYPE_INT }, \ + { "larval_lifetime", CTLTYPE_INT }, \ + { "blockacq_count", CTLTYPE_INT }, \ + { "blockacq_lifetime", CTLTYPE_INT }, \ +} + +#define KEYCTL_VARS { \ + 0, \ + &key_debug_level, \ + &key_spi_trycnt, \ + &key_spi_minval, \ + &key_spi_maxval, \ + &key_int_random, \ + &key_larval_lifetime, \ + &key_blockacq_count, \ + &key_blockacq_lifetime, \ +} + +#define _ARRAYLEN(p) (sizeof(p)/sizeof(p[0])) +#define _KEYLEN(key) ((u_int)((key)->sadb_key_bits >> 3)) +#define _KEYBITS(key) ((u_int)((key)->sadb_key_bits)) +#define _KEYBUF(key) ((caddr_t)((caddr_t)(key) + sizeof(struct sadb_key))) + +#define _INADDR(in) ((struct sockaddr_in *)(in)) + +/* should not ifdef kernel opt in kernel header file */ +#if !defined(KERNEL) && !defined(_KERNEL) +#if defined(INET6) +#define _IN6ADDR(in6) ((struct sockaddr_in6 *)(in6)) +#define _SALENBYAF(family) \ + (((family) == AF_INET) ? \ + (u_int)sizeof(struct sockaddr_in) : \ + (u_int)sizeof(struct sockaddr_in6)) +#define _INALENBYAF(family) \ + (((family) == AF_INET) ? \ + (u_int)sizeof(struct in_addr) : \ + (u_int)sizeof(struct in6_addr)) +#define _INADDRBYSA(saddr) \ + ((((struct sockaddr *)(saddr))->sa_family == AF_INET) ? \ + (caddr_t)&((struct sockaddr_in *)(saddr))->sin_addr : \ + (caddr_t)&((struct sockaddr_in6 *)(saddr))->sin6_addr) +#define _INPORTBYSA(saddr) \ + ((((struct sockaddr *)(saddr))->sa_family == AF_INET) ? \ + ((struct sockaddr_in *)(saddr))->sin_port : \ + ((struct sockaddr_in6 *)(saddr))->sin6_port) +#else +#define _IN6ADDR(in6) "#error" +#define _SALENBYAF(family) sizeof(struct sockaddr_in) +#define _INALENBYAF(family) sizeof(struct in_addr) +#define _INADDRBYSA(saddr) ((caddr_t)&((struct sockaddr_in *)(saddr))->sin_addr) +#define _INPORTBYSA(saddr) (((struct sockaddr_in *)(saddr))->sin_port) +#endif /* defined(INET6) */ +#endif /* !defined(KERNEL) && !defined(_KERNEL) */ + +#endif /* _NETKEY_KEY_VAR_H_ */ diff --git a/sys/netkey/keydb.h b/sys/netkey/keydb.h new file mode 100644 index 0000000..9574a44 --- /dev/null +++ b/sys/netkey/keydb.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETKEY_KEYDB_H_ +#define _NETKEY_KEYDB_H_ + +#ifdef KERNEL + +/* Security Assocciation Index */ +/* NOTE: Encure to be same address family */ +struct secasindex { + struct sockaddr_storage src; /* srouce address for SA */ + struct sockaddr_storage dst; /* destination address for SA */ + u_int16_t proto; /* IPPROTO_ESP or IPPROTO_AH */ + u_int8_t mode; /* mode of protocol, see ipsec.h */ +}; + +/* Security Association Data Base */ +struct secashead { + LIST_ENTRY(secashead) chain; + + struct secasindex saidx; + struct secpolicyindex *owner; /* Indicate it who owned its SA. */ + /* If NULL then it's shared SA */ + + u_int8_t state; /* MATURE or DEAD. */ + LIST_HEAD(_satree, secasvar) savtree[SADB_SASTATE_MAX+1]; + /* SA chain */ + /* The first of this list is newer SA */ + + struct route sa_route; /* XXX */ +}; + +/* Security Association */ +struct secasvar { + LIST_ENTRY(secasvar) chain; + + int refcnt; /* reference count */ + u_int8_t state; /* Status of this Association */ + + u_int8_t alg_auth; /* Authentication Algorithm Identifier*/ + u_int8_t alg_enc; /* Cipher Algorithm Identifier */ + u_int32_t spi; /* SPI Value, network byte order */ + u_int32_t flags; /* holder for SADB_KEY_FLAGS */ + + struct sadb_key *key_auth; /* Key for Authentication */ + /* length has been shifted up to 3. */ + struct sadb_key *key_enc; /* Key for Encryption */ + /* length has been shifted up to 3. */ + caddr_t iv; /* Initilization Vector */ + u_int ivlen; /* length of IV */ + + struct secreplay *replay; /* replay prevention */ + u_int32_t tick; /* for lifetime */ + + struct sadb_lifetime *lft_c; /* CURRENT lifetime, it's constant. */ + struct sadb_lifetime *lft_h; /* HARD lifetime */ + struct sadb_lifetime *lft_s; /* SOFT lifetime */ + + u_int32_t seq; /* sequence number */ + pid_t pid; /* message's pid */ + + struct secashead *sah; /* back pointer to the secashead */ +}; + +/* replay prevention */ +struct secreplay { + u_int32_t count; + u_int wsize; /* window size, i.g. 4 bytes */ + u_int32_t seq; /* used by sender */ + u_int32_t lastseq; /* used by receiver */ + caddr_t bitmap; /* used by receiver */ +}; + +/* socket table due to send PF_KEY messages. */ +struct secreg { + LIST_ENTRY(secreg) chain; + + struct socket *so; +}; + +#ifndef IPSEC_NONBLOCK_ACQUIRE +/* acquiring list table. */ +struct secacq { + LIST_ENTRY(secacq) chain; + + struct secasindex saidx; + + u_int32_t seq; /* sequence number */ + u_int32_t tick; /* for lifetime */ + int count; /* for lifetime */ +}; +#endif + +/* Sensitivity Level Specification */ +/* nothing */ + +#define SADB_KILL_INTERVAL 600 /* six seconds */ + +struct key_cb { + int key_count; + int any_count; +}; + +#endif /* KERNEL */ + +#endif /* _NETKEY_KEYDB_H_ */ diff --git a/sys/netkey/keysock.h b/sys/netkey/keysock.h new file mode 100644 index 0000000..d71e5aa --- /dev/null +++ b/sys/netkey/keysock.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* $Id: keysock.h,v 1.1.6.3.6.1 1999/05/17 17:03:19 itojun Exp $ */ + +#ifndef _NETKEY_KEYSOCK_H_ +#define _NETKEY_KEYSOCK_H_ + +#if defined(KERNEL) +struct keycb { + struct rawcb kp_raw; /* rawcb */ + int kp_promisc; /* promiscuous mode */ + int kp_registered; /* registered socket */ +}; + +extern int key_output __P((struct mbuf *, ...)); +extern int key_usrreq __P((struct socket *, int, struct mbuf *, + struct mbuf *, struct mbuf *)); + +#define KEY_SENDUP_ONE 0 +#define KEY_SENDUP_ALL 1 +#define KEY_SENDUP_REGISTERED 2 + +extern int key_sendup __P((struct socket *, struct sadb_msg *, u_int, + int)); +#endif /* defined(KERNEL) */ + +#endif _NETKEY_KEYSOCK_H_ diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 57ddca7..f7ff74c 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -355,6 +355,12 @@ struct socket * int sooptcopyin __P((struct sockopt *sopt, void *buf, size_t len, size_t minlen)); int sooptcopyout __P((struct sockopt *sopt, void *buf, size_t len)); + +/* XXX; prepare mbuf for (__FreeBSD__ < 3) routines. */ +int soopt_getm __P((struct sockopt *sopt, struct mbuf **mp)); +int soopt_mcopyin __P((struct sockopt *sopt, struct mbuf *m)); +int soopt_mcopyout __P((struct sockopt *sopt, struct mbuf *m)); + int sopoll __P((struct socket *so, int events, struct ucred *cred, struct proc *p)); int soreceive __P((struct socket *so, struct sockaddr **paddr, -- cgit v1.1