diff options
-rw-r--r-- | sys/netinet/ip_esp_new.c | 8 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.c | 121 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.h | 60 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 567 |
4 files changed, 421 insertions, 335 deletions
diff --git a/sys/netinet/ip_esp_new.c b/sys/netinet/ip_esp_new.c index 2f37fea483d..0c5c4e5d8ad 100644 --- a/sys/netinet/ip_esp_new.c +++ b/sys/netinet/ip_esp_new.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_esp_new.c,v 1.47 1999/10/29 02:10:01 angelos Exp $ */ +/* $OpenBSD: ip_esp_new.c,v 1.48 1999/12/04 23:20:21 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -36,8 +36,7 @@ */ /* - * Based on draft-ietf-ipsec-esp-v2-00.txt and - * draft-ietf-ipsec-ciph-{des,3des}-{derived,expiv}-00.txt + * RFC 2406. */ #include <sys/param.h> @@ -261,6 +260,9 @@ esp_new_input(struct mbuf *m, struct tdb *tdb) u_int32_t btsx; union authctx ctx; u_char buf[AH_ALEN_MAX], buf2[AH_ALEN_MAX]; +#if INET6 + struct ipv6 *ipv6, ipv6o; +#endif /* INET6 */ blks = espx->blocksize; diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c index 500e4042b04..eda5bd4b8f3 100644 --- a/sys/netinet/ip_ipsp.c +++ b/sys/netinet/ip_ipsp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.c,v 1.55 1999/11/04 11:20:05 ho Exp $ */ +/* $OpenBSD: ip_ipsp.c,v 1.56 1999/12/04 23:20:21 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -202,6 +202,7 @@ get_sa_require(struct inpcb *inp) * in that case use dst. */ +/* XXX Make IPv6 compliant */ int check_ipsec_policy(struct inpcb *inp, u_int32_t daddr) { @@ -285,9 +286,16 @@ check_ipsec_policy(struct inpcb *inp, u_int32_t daddr) /* If necessary try to notify keymanagement three times */ while (i < 3) { - /* XXX address */ - DPRINTF(("ipsec: send SA request (%d), remote ip: %s, SA type: %d\n", - i + 1, inet_ntoa4(dst->sen_ip_dst), sa_require)); + switch (dst->sen_type) + { + case SENT_IPSP: + DPRINTF(("ipsec: send SA request (%d), remote IPv4 address: %s, SA type: %d\n", i + 1, inet_ntoa4(dst->sen_ip_dst), sa_require)); + break; + + case SENT_IPSP6: + DPRINTF(("ipsec: send SA request (%d), remote IPv6 address: %s, SA type: %d\n", i + 1, inet6_ntoa4(dst->sen_ip6_dst), sa_require)); + break; + } /* Send notify */ /* XXX PF_KEYv2 Notify */ @@ -326,6 +334,7 @@ check_ipsec_policy(struct inpcb *inp, u_int32_t daddr) * Add an inpcb to the list of inpcb which reference this tdb directly. */ +/* XXX Make IPv6 compliant */ void tdb_add_inp(struct tdb *tdb, struct inpcb *inp) { @@ -1049,29 +1058,70 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) bzero(&encapdst, sizeof(struct sockaddr_encap)); bzero(&encapnetmask, sizeof(struct sockaddr_encap)); - encapdst.sen_len = SENT_IP4_LEN; encapdst.sen_family = PF_KEY; - encapdst.sen_type = SENT_IP4; - encapdst.sen_ip_src = tdbp->tdb_flow->flow_src.sin.sin_addr; - encapdst.sen_ip_dst = tdbp->tdb_flow->flow_dst.sin.sin_addr; - encapdst.sen_proto = tdbp->tdb_flow->flow_proto; - encapdst.sen_sport = tdbp->tdb_flow->flow_src.sin.sin_port; - encapdst.sen_dport = tdbp->tdb_flow->flow_dst.sin.sin_port; - - encapnetmask.sen_len = SENT_IP4_LEN; encapnetmask.sen_family = PF_KEY; - encapnetmask.sen_type = SENT_IP4; - encapnetmask.sen_ip_src = tdbp->tdb_flow->flow_srcmask.sin.sin_addr; - encapnetmask.sen_ip_dst = tdbp->tdb_flow->flow_dstmask.sin.sin_addr; - - if (tdbp->tdb_flow->flow_proto) - { - encapnetmask.sen_proto = 0xff; - if (tdbp->tdb_flow->flow_src.sin.sin_port) - encapnetmask.sen_sport = 0xffff; - if (tdbp->tdb_flow->flow_dst.sin.sin_port) - encapnetmask.sen_dport = 0xffff; - } + + switch (tdbp->tdb_flow->flow_src.sa.sa_family) + { + case AF_INET: + encapdst.sen_len = SENT_IP4_LEN; + encapdst.sen_type = SENT_IP4; + encapdst.sen_ip_src = tdbp->tdb_flow->flow_src.sin.sin_addr; + encapdst.sen_ip_dst = tdbp->tdb_flow->flow_dst.sin.sin_addr; + encapdst.sen_proto = tdbp->tdb_flow->flow_proto; + encapdst.sen_sport = tdbp->tdb_flow->flow_src.sin.sin_port; + encapdst.sen_dport = tdbp->tdb_flow->flow_dst.sin.sin_port; + + encapnetmask.sen_ip_src = tdbp->tdb_flow->flow_srcmask.sin.sin_addr; + encapnetmask.sen_ip_dst = tdbp->tdb_flow->flow_dstmask.sin.sin_addr; + + /* Mask transport protocol and ports if applicable */ + if (tdbp->tdb_flow->flow_proto) + { + encapnetmask.sen_proto = 0xff; + if (tdbp->tdb_flow->flow_src.sin.sin_port) + encapnetmask.sen_sport = 0xffff; + if (tdbp->tdb_flow->flow_dst.sin.sin_port) + encapnetmask.sen_dport = 0xffff; + } + break; + +#if INET6 + case AF_INET6: + encapdst.sen_len = SENT_IP6_LEN; + encapdst.sen_type = SENT_IP6; + encapdst.sen_ip6_src = tdbp->tdb_flow->flow_src.sin6.sin6_addr; + encapdst.sen_ip6_dst = tdbp->tdb_flow->flow_dst.sin6.sin6_addr; + encapdst.sen_ip6_proto = tdbp->tdb_flow->flow_proto; + encapdst.sen_ip6_sport = tdbp->tdb_flow->flow_src.sin6.sin6_port; + encapdst.sen_ip6_dport = tdbp->tdb_flow->flow_dst.sin6.sin6_port; + + encapnetmask.sen_ip6_src = tdbp->tdb_flow->flow_srcmask.sin6.sin6_addr; + encapnetmask.sen_ip6_dst = tdbp->tdb_flow->flow_dstmask.sin6.sin6_addr; + + /* Mask transport protocol and ports if applicable */ + if (tdbp->tdb_flow->flow_proto) + { + encapnetmask.sen_ip6_proto = 0xff; + if (tdbp->tdb_flow->flow_src.sin6.sin6_port) + encapnetmask.sen_ip6_sport = 0xffff; + if (tdbp->tdb_flow->flow_dst.sin6.sin6_port) + encapnetmask.sen_ip6_dport = 0xffff; + } + break; +#endif /* INET6 */ + + default: +#ifdef DIAGNOSTIC + panic("tdb_delete(): SA %s/%08x/%d has flow of unknown type %d", ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi), tdbp->tdb_sproto, tdbp->tdb_flow->flow_src.sa.sa_family); +#endif /* DIAGNOSTIC */ + delete_flow(tdbp->tdb_flow, tdbp); + continue; + } + + /* Always the same type for address and netmask */ + encapnetmask.sen_len = encapdst.sen_len; + encapnetmask.sen_type = encapdst.sen_type; rtrequest(RTM_DELETE, (struct sockaddr *) &encapdst, (struct sockaddr *) 0, @@ -1396,7 +1446,7 @@ ipsp_kern(int off, char **bufp, int len) char * inet_ntoa4(struct in_addr ina) { - static char buf[4][4 * sizeof "123"]; + static char buf[4][4 * sizeof "123" + 4]; unsigned char *ucp = (unsigned char *) &ina; static int i = 3; @@ -1406,6 +1456,22 @@ inet_ntoa4(struct in_addr ina) return (buf[i]); } +#if INET6 +char * +inet6_ntoa4(struct in6_addr ina) +{ + static char buf[4][8 * sizeof "abcd" + 8]; + unsigned char *ucp = (unsigned char *) &ina; + static int i = 3; + + i = (i + 1) % 4; + sprintf(buf[i], "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + ucp[0] & 0xff, ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff, + ucp[4] & 0xff, ucp[5] & 0xff, ucp[6] & 0xff, ucp[7] & 0xff); + return (buf[i]); +} +#endif /* INET6 */ + char * ipsp_address(union sockaddr_union sa) { @@ -1415,7 +1481,8 @@ ipsp_address(union sockaddr_union sa) return inet_ntoa4(sa.sin.sin_addr); #if INET6 - /* XXX Add AF_INET6 support here */ + case AF_INET6: + return inet_ntoa6(sa.sin6.s6_addr); #endif /* INET6 */ default: diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h index 62c6ad8b1a6..b3a06d3d7cc 100644 --- a/sys/netinet/ip_ipsp.h +++ b/sys/netinet/ip_ipsp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.h,v 1.46 1999/10/29 05:20:46 angelos Exp $ */ +/* $OpenBSD: ip_ipsp.h,v 1.47 1999/12/04 23:20:21 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -123,6 +123,16 @@ struct sockaddr_encap u_int8_t Filler[3]; } Sip4; + struct /* SENT_IP6 */ + { + struct in6_addr Src; + struct in6_addr Dst; + u_int16_t Sport; + u_int16_t Dport; + u_int8_t Proto; + u_int8_t Filler[3]; + } Sip6; + struct /* SENT_IPSP */ { struct in_addr Dst; @@ -130,18 +140,34 @@ struct sockaddr_encap u_int8_t Sproto; u_int8_t Filler[7]; } Sipsp; + + struct /* SENT_IPSP6 */ + { + struct in6_addr Dst; + u_int32_t Spi; + u_int8_t Sproto; + u_int8_t Filler[7]; + } Sipsp6; } Sen; }; -#define sen_data Sen.Data -#define sen_ip_src Sen.Sip4.Src -#define sen_ip_dst Sen.Sip4.Dst -#define sen_proto Sen.Sip4.Proto -#define sen_sport Sen.Sip4.Sport -#define sen_dport Sen.Sip4.Dport -#define sen_ipsp_dst Sen.Sipsp.Dst -#define sen_ipsp_spi Sen.Sipsp.Spi -#define sen_ipsp_sproto Sen.Sipsp.Sproto +#define sen_data Sen.Data +#define sen_ip_src Sen.Sip4.Src +#define sen_ip_dst Sen.Sip4.Dst +#define sen_proto Sen.Sip4.Proto +#define sen_sport Sen.Sip4.Sport +#define sen_dport Sen.Sip4.Dport +#define sen_ip6_src Sen.Sip6.Src +#define sen_ip6_dst Sen.Sip6.Dst +#define sen_ip6_proto Sen.Sip6.Proto +#define sen_ip6_sport Sen.Sip6.Sport +#define sen_ip6_dport Sen.Sip6.Dport +#define sen_ipsp_dst Sen.Sipsp.Dst +#define sen_ipsp_spi Sen.Sipsp.Spi +#define sen_ipsp_sproto Sen.Sipsp.Sproto +#define sen_ipsp6_dst Sen.Sipsp6.Dst +#define sen_ipsp6_spi Sen.Sipsp6.Spi +#define sen_ipsp6_sproto Sen.Sipsp6.Sproto /* * The "type" is really part of the address as far as the routing @@ -152,7 +178,9 @@ struct sockaddr_encap */ #define SENT_IP4 0x0001 /* data is two struct in_addr */ -#define SENT_IPSP 0x0002 /* data as in IP4 plus SPI */ +#define SENT_IPSP 0x0002 /* data as in IP4/6 plus SPI */ +#define SENT_IP6 0x0004 +#define SENT_IPSP6 0x0008 /* * SENT_HDRLEN is the length of the "header" @@ -165,8 +193,13 @@ struct sockaddr_encap #define SENT_IP4_SRCOFF (0) #define SENT_IP4_DSTOFF (sizeof (struct in_addr)) +#define SENT_IP6_SRCOFF (0) +#define SENT_IP6_DSTOFF (sizeof (struct in6_addr)) + #define SENT_IP4_LEN 20 #define SENT_IPSP_LEN 20 +#define SENT_IP6_LEN 44 +#define SENT_IPSP6_LEN 32 #define NOTIFY_SOFT_EXPIRE 0 /* Soft expiration of SA */ #define NOTIFY_HARD_EXPIRE 1 /* Hard expiration of SA */ @@ -434,6 +467,11 @@ extern struct xformsw xformsw[], *xformswNXFORMSW; /* Misc. */ extern char *inet_ntoa4(struct in_addr); + +#if INET6 +extern char *inet6_ntoa4(struct in6_addr); +#endif /* INET6 */ + extern char *ipsp_address(union sockaddr_union); /* TDB management routines */ diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 277a0608ed9..ea4b083e0db 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_output.c,v 1.52 1999/11/04 11:21:14 ho Exp $ */ +/* $OpenBSD: ip_output.c,v 1.53 1999/12/04 23:20:21 angelos Exp $ */ /* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */ /* @@ -129,6 +129,12 @@ ip_output(m0, va_alist) struct udphdr *udp; struct tcphdr *tcp; struct inpcb *inp; + + struct route_enc re0, *re = &re0; + struct sockaddr_encap *ddst, *gw; + u_int8_t sa_require, sa_have = 0; + struct tdb *tdb, *t; + int s; #endif va_start(ap, m0); @@ -166,21 +172,187 @@ ip_output(m0, va_alist) hlen = ip->ip_hl << 2; } + /* + * Route packet. + */ + if (ro == 0) { + ro = &iproute; + bzero((caddr_t)ro, sizeof (*ro)); + } + dst = satosin(&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 || + dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)0; + } + if (ro->ro_rt == 0) { + dst->sin_family = AF_INET; + dst->sin_len = sizeof(*dst); + dst->sin_addr = ip->ip_dst; + } + /* + * If routing to interface only, + * short circuit routing lookup. + */ + if (flags & IP_ROUTETOIF) { + if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 && + (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) { + ipstat.ips_noroute++; + error = ENETUNREACH; + goto bad; + } + ifp = ia->ia_ifp; + ip->ip_ttl = 1; + } else { + if (ro->ro_rt == 0) + rtalloc(ro); + if (ro->ro_rt == 0) { + ipstat.ips_noroute++; + error = EHOSTUNREACH; + goto bad; + } + ia = ifatoia(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 = satosin(ro->ro_rt->rt_gateway); + } + if (IN_MULTICAST(ip->ip_dst.s_addr)) { + struct in_multi *inm; + + m->m_flags |= M_MCAST; + /* + * IP destination address is multicast. Make sure "dst" + * still points to the address in "ro". (It may have been + * changed to point to a gateway address, above.) + */ + dst = satosin(&ro->ro_dst); + /* + * See if the caller provided any multicast options + */ + if (imo != NULL) { + ip->ip_ttl = imo->imo_multicast_ttl; + if (imo->imo_multicast_ifp != NULL) + ifp = imo->imo_multicast_ifp; + } else + ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; + /* + * Confirm that the outgoing interface supports multicast. + */ + if ((ifp->if_flags & IFF_MULTICAST) == 0) { + ipstat.ips_noroute++; + error = ENETUNREACH; + goto bad; + } + /* + * If source address not specified yet, use address + * of outgoing interface. + */ + if (ip->ip_src.s_addr == INADDR_ANY) { + register struct in_ifaddr *ia; + + for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) + if (ia->ia_ifp == ifp) { + ip->ip_src = ia->ia_addr.sin_addr; + break; + } + } + + IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); + if (inm != NULL && + (imo == NULL || imo->imo_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. + */ + ip_mloopback(ifp, m, dst); + } +#ifdef MROUTING + else { + /* + * If we are acting as a multicast router, perform + * multicast forwarding as if the packet had just + * arrived on the interface to which we are about + * to send. The multicast forwarding function + * recursively calls this function, using the + * IP_FORWARDING flag to prevent infinite recursion. + * + * Multicasts that are looped back by ip_mloopback(), + * above, will be forwarded by the ip_input() routine, + * if necessary. + */ + extern struct socket *ip_mrouter; + + if (ip_mrouter && (flags & IP_FORWARDING) == 0) { + if (ip_mforward(m, ifp) != 0) { + m_freem(m); + goto done; + } + } + } +#endif + /* + * Multicasts with a time-to-live 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 ip_mloopback() will + * loop back a copy if this host actually belongs to the + * destination group on the loopback interface. + */ + if (ip->ip_ttl == 0 || (ifp->if_flags & IFF_LOOPBACK) != 0) { + m_freem(m); + goto done; + } + + goto sendit; + } +#ifndef notdef + /* + * If source address not specified yet, use address + * of outgoing interface. + */ + if (ip->ip_src.s_addr == INADDR_ANY) + ip->ip_src = ia->ia_addr.sin_addr; +#endif + /* + * Look for broadcast address and + * and verify user is allowed to send + * such a packet. + */ + if (in_broadcast(dst->sin_addr, ifp)) { + if ((ifp->if_flags & IFF_BROADCAST) == 0) { + error = EADDRNOTAVAIL; + goto bad; + } + if ((flags & IP_ALLOWBROADCAST) == 0) { + error = EACCES; + goto bad; + } + /* don't allow broadcast messages to be fragmented */ + if ((u_int16_t)ip->ip_len > ifp->if_mtu) { + error = EMSGSIZE; + goto bad; + } + m->m_flags |= M_BCAST; + } else + m->m_flags &= ~M_BCAST; + +sendit: #ifdef IPSEC /* - * Check if the packet needs encapsulation + * Check if the packet needs encapsulation. */ if (!(flags & IP_ENCAPSULATED) && (inp == NULL || inp->inp_seclevel[SL_AUTH] != IPSEC_LEVEL_BYPASS || inp->inp_seclevel[SL_ESP_TRANS] != IPSEC_LEVEL_BYPASS || inp->inp_seclevel[SL_ESP_NETWORK] != IPSEC_LEVEL_BYPASS)) { - struct route_enc re0, *re = &re0; - struct sockaddr_encap *ddst, *gw; - struct tdb *tdb, *t; - int s; - u_int8_t sa_require, sa_have = 0; - if (inp == NULL) sa_require = get_sa_require(inp); else @@ -193,10 +365,13 @@ ip_output(m0, va_alist) * lower the level, and udp_output calls us in splnet(). */ s = splnet(); - /* Check if there was a bound outgoing SA */ + + /* + * Check if there was an outgoing SA bound to the flow + * from a transport protocol. + */ if (inp && inp->inp_tdb && - (inp->inp_tdb->tdb_dst.sin.sin_addr.s_addr == - INADDR_ANY || + (inp->inp_tdb->tdb_dst.sin.sin_addr.s_addr == INADDR_ANY || !bcmp(&inp->inp_tdb->tdb_dst.sin.sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)))) { tdb = inp->inp_tdb; @@ -257,7 +432,8 @@ ip_output(m0, va_alist) /* * There might be a specific route, that tells us to avoid * doing IPsec; this is useful for specific routes that we - * don't want to have IPsec applied on. + * don't want to have IPsec applied on, like the key + * management ports. */ if ((gw != NULL) && (gw->sen_ipsp_dst.s_addr == 0) && @@ -266,7 +442,9 @@ ip_output(m0, va_alist) goto no_encap; } - if (gw == NULL || gw->sen_type != SENT_IPSP) { + /* Sanity check */ + if (gw == NULL || ((gw->sen_type != SENT_IPSP) && + (gw->sen_type != SENT_IPSP6))) { splx(s); DPRINTF(("ip_output(): no gw or gw data not IPSP\n")); @@ -277,50 +455,6 @@ ip_output(m0, va_alist) goto done; } - /* - * For VPNs a route with a reserved SPI of 0 is used to - * indicate the need for an SA when none is established. - */ - if (ntohl(gw->sen_ipsp_spi) == SPI_LOCAL_USE) { - bzero(&sunion, sizeof(sunion)); - sunion.sin.sin_family = AF_INET; - sunion.sin.sin_len = sizeof(struct sockaddr_in); - sunion.sin.sin_addr = gw->sen_ipsp_dst; - tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion, - gw->sen_ipsp_sproto); - - if (tdb) - { - if (tdb->tdb_authalgxform) - sa_require = NOTIFY_SATYPE_AUTH; - if (tdb->tdb_encalgxform) - sa_require |= NOTIFY_SATYPE_CONF; - if (tdb->tdb_flags & TDBF_TUNNELING) - sa_require |= NOTIFY_SATYPE_TUNNEL; - } - else /* No TDB found */ - { - /* - * XXX We should construct a TDB from system - * default (which should be tunable via sysctl). - * For now, drop packet and ignore SPD entry. - */ - splx(s); - goto no_encap; - } - - /* PF_KEYv2 notification message */ - if (tdb->tdb_satype != SADB_X_SATYPE_BYPASS) - pfkeyv2_acquire(tdb, 0); /* XXX Check for errors */ - - splx(s); - - /* - * When sa_require is set, the packet will be dropped - * at no_encap. - */ - goto no_encap; - } /* * At this point we have an IPSP "gateway" (tunnel) spec. @@ -330,13 +464,63 @@ ip_output(m0, va_alist) * to the appropriate transformation. */ bzero(&sunion, sizeof(sunion)); - sunion.sin.sin_family = AF_INET; - sunion.sin.sin_len = sizeof(struct sockaddr_in); - sunion.sin.sin_addr = gw->sen_ipsp_dst; + + if (gw->sen_type == SENT_IPSP) { + sunion.sin.sin_family = AF_INET; + sunion.sin.sin_len = sizeof(struct sockaddr_in); + sunion.sin.sin_addr = gw->sen_ipsp_dst; + } +#if INET6 + else { + sunion.sin6.sin6_family = AF_INET6; + sunion.sin6.sin6_len = sizeof(struct sockaddr_in6); + sunion.sin6.sin6_addr = gw->sen_ipsp6_dst; + } +#endif /* INET6 */ + tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion, gw->sen_ipsp_sproto); - have_tdb: + /* + * For VPNs a route with a reserved SPI is used to + * indicate the need for an SA when none is established. + */ + if (((ntohl(gw->sen_ipsp_spi) == SPI_LOCAL_USE) && + (gw->sen_type == SENT_IPSP)) || + ((ntohl(gw->sen_ipsp6_spi) == SPI_LOCAL_USE) && + (gw->sen_type == SENT_IPSP6))) { + if (tdb == NULL) { + /* + * XXX We should construct a TDB from system + * default (which should be tunable via sysctl). + * For now, drop packet and ignore SPD entry. + */ + splx(s); + goto no_encap; + } + else { + if (tdb->tdb_authalgxform) + sa_require = NOTIFY_SATYPE_AUTH; + if (tdb->tdb_encalgxform) + sa_require |= NOTIFY_SATYPE_CONF; + if (tdb->tdb_flags & TDBF_TUNNELING) + sa_require |= NOTIFY_SATYPE_TUNNEL; + } + + /* PF_KEYv2 notification message */ + if (tdb && tdb->tdb_satype != SADB_X_SATYPE_BYPASS) + pfkeyv2_acquire(tdb, 0); /* XXX Check for errors */ + + splx(s); + + /* + * When sa_require is set, the packet will be dropped + * at no_encap. + */ + goto no_encap; + } + + have_tdb: ip->ip_len = htons((u_short)ip->ip_len); ip->ip_off = htons((u_short)ip->ip_off); @@ -353,7 +537,12 @@ ip_output(m0, va_alist) if (tdb == NULL) { splx(s); - DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet_ntoa4(gw->sen_ipsp_dst), ntohl(gw->sen_ipsp_spi), gw->sen_ipsp_sproto)); + if (gw->sen_type == SENT_IPSP) + DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet_ntoa4(gw->sen_ipsp_dst), ntohl(gw->sen_ipsp_spi), gw->sen_ipsp_sproto)); +#if INET6 + else + DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet_ntoa4(gw->sen_ipsp_dst), ntohl(gw->sen_ipsp_spi), gw->sen_ipsp_sproto)); +#endif /* INET6 */ if (re->re_rt) RTFREE(re->re_rt); @@ -374,53 +563,6 @@ ip_output(m0, va_alist) goto done; } - /* Fix the ip_src field if necessary */ - if (ip->ip_src.s_addr == INADDR_ANY) { - if (tdb && tdb->tdb_src.sin.sin_addr.s_addr != 0 && - tdb->tdb_src.sa.sa_family == AF_INET) - ip->ip_src = tdb->tdb_src.sin.sin_addr; - else - { - if (ro == 0) { - ro = &iproute; - bzero((caddr_t)ro, sizeof (*ro)); - } - - dst = satosin(&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 || - dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { - RTFREE(ro->ro_rt); - ro->ro_rt = (struct rtentry *)0; - } - - if (ro->ro_rt == 0) { - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = ip->ip_dst; - rtalloc(ro); - } - - if (ro->ro_rt == 0) { - splx(s); - ipstat.ips_noroute++; - error = EHOSTUNREACH; - m_freem(m); - goto done; - } - - ia = ifatoia(ro->ro_rt->rt_ifa); - ro->ro_rt->rt_use++; - ip->ip_src = ia->ia_addr.sin_addr; - } - } - while (tdb && tdb->tdb_xform) { /* Check if the SPI is invalid */ if (tdb->tdb_flags & TDBF_INVALID) { @@ -432,27 +574,35 @@ ip_output(m0, va_alist) return ENXIO; } + /* Sanity check */ + if (tdb->tdb_dst.sa.sa_family != AF_INET) { + splx(s); + DPRINTF(("ip_output(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family)); + m_freem(m); + if (re->re_rt) + RTFREE(re->re_rt); + return ENXIO; + } + /* Register first use, setup expiration timer */ if (tdb->tdb_first_use == 0) { tdb->tdb_first_use = time.tv_sec; tdb_expiration(tdb, TDBEXP_TIMEOUT); } - - /* Check for tunneling */ - if (((tdb->tdb_dst.sin.sin_addr.s_addr != - INADDR_ANY && - tdb->tdb_dst.sin.sin_addr.s_addr != - ip->ip_dst.s_addr) || - (tdb->tdb_flags & TDBF_TUNNELING)) && - (tdb->tdb_xform->xf_type != XF_IP4)) - { - DPRINTF(("ip_output(): tunneling\n")); + /* Check for tunneling */ + if (((tdb->tdb_dst.sa.sa_family == AF_INET) && + (tdb->tdb_dst.sin.sin_addr.s_addr != + INADDR_ANY) && + (tdb->tdb_dst.sin.sin_addr.s_addr != + ip->ip_dst.s_addr)) || + ((tdb->tdb_flags & TDBF_TUNNELING) && + (tdb->tdb_xform->xf_type != XF_IP4))) { /* * Fix checksum here, AH and ESP fix the * checksum in their output routines. */ - ip->ip_sum = in_cksum(m, hlen); + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); error = ipe4_output(m, tdb, &mp); if (mp == NULL) error = EFAULT; @@ -472,9 +622,10 @@ ip_output(m0, va_alist) * output routines. */ ip = mtod(m, struct ip *); - ip->ip_sum = in_cksum(m, hlen); + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); } + error = (*(tdb->tdb_xform->xf_output))(m, tdb, &mp); if (!error && mp == NULL) error = EFAULT; @@ -507,7 +658,7 @@ ip_output(m0, va_alist) NTOHS(ip->ip_len); NTOHS(ip->ip_off); return ip_output(m, NULL, NULL, - IP_ENCAPSULATED | IP_RAWOUTPUT, NULL, NULL); + IP_ENCAPSULATED | IP_RAWOUTPUT, NULL, NULL); no_encap: /* This is for possible future use, don't move or delete */ @@ -522,178 +673,6 @@ no_encap: } #endif /* IPSEC */ - /* - * Route packet. - */ - if (ro == 0) { - ro = &iproute; - bzero((caddr_t)ro, sizeof (*ro)); - } - dst = satosin(&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 || - dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { - RTFREE(ro->ro_rt); - ro->ro_rt = (struct rtentry *)0; - } - if (ro->ro_rt == 0) { - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = ip->ip_dst; - } - /* - * If routing to interface only, - * short circuit routing lookup. - */ - if (flags & IP_ROUTETOIF) { - if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 && - (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) { - ipstat.ips_noroute++; - error = ENETUNREACH; - goto bad; - } - ifp = ia->ia_ifp; - ip->ip_ttl = 1; - } else { - if (ro->ro_rt == 0) - rtalloc(ro); - if (ro->ro_rt == 0) { - ipstat.ips_noroute++; - error = EHOSTUNREACH; - goto bad; - } - ia = ifatoia(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 = satosin(ro->ro_rt->rt_gateway); - } - if (IN_MULTICAST(ip->ip_dst.s_addr)) { - struct in_multi *inm; - - m->m_flags |= M_MCAST; - /* - * IP destination address is multicast. Make sure "dst" - * still points to the address in "ro". (It may have been - * changed to point to a gateway address, above.) - */ - dst = satosin(&ro->ro_dst); - /* - * See if the caller provided any multicast options - */ - if (imo != NULL) { - ip->ip_ttl = imo->imo_multicast_ttl; - if (imo->imo_multicast_ifp != NULL) - ifp = imo->imo_multicast_ifp; - } else - ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; - /* - * Confirm that the outgoing interface supports multicast. - */ - if ((ifp->if_flags & IFF_MULTICAST) == 0) { - ipstat.ips_noroute++; - error = ENETUNREACH; - goto bad; - } - /* - * If source address not specified yet, use address - * of outgoing interface. - */ - if (ip->ip_src.s_addr == INADDR_ANY) { - register struct in_ifaddr *ia; - - for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) - if (ia->ia_ifp == ifp) { - ip->ip_src = ia->ia_addr.sin_addr; - break; - } - } - - IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); - if (inm != NULL && - (imo == NULL || imo->imo_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. - */ - ip_mloopback(ifp, m, dst); - } -#ifdef MROUTING - else { - /* - * If we are acting as a multicast router, perform - * multicast forwarding as if the packet had just - * arrived on the interface to which we are about - * to send. The multicast forwarding function - * recursively calls this function, using the - * IP_FORWARDING flag to prevent infinite recursion. - * - * Multicasts that are looped back by ip_mloopback(), - * above, will be forwarded by the ip_input() routine, - * if necessary. - */ - extern struct socket *ip_mrouter; - - if (ip_mrouter && (flags & IP_FORWARDING) == 0) { - if (ip_mforward(m, ifp) != 0) { - m_freem(m); - goto done; - } - } - } -#endif - /* - * Multicasts with a time-to-live 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 ip_mloopback() will - * loop back a copy if this host actually belongs to the - * destination group on the loopback interface. - */ - if (ip->ip_ttl == 0 || (ifp->if_flags & IFF_LOOPBACK) != 0) { - m_freem(m); - goto done; - } - - goto sendit; - } -#ifndef notdef - /* - * If source address not specified yet, use address - * of outgoing interface. - */ - if (ip->ip_src.s_addr == INADDR_ANY) - ip->ip_src = ia->ia_addr.sin_addr; -#endif - /* - * Look for broadcast address and - * and verify user is allowed to send - * such a packet. - */ - if (in_broadcast(dst->sin_addr, ifp)) { - if ((ifp->if_flags & IFF_BROADCAST) == 0) { - error = EADDRNOTAVAIL; - goto bad; - } - if ((flags & IP_ALLOWBROADCAST) == 0) { - error = EACCES; - goto bad; - } - /* don't allow broadcast messages to be fragmented */ - if ((u_int16_t)ip->ip_len > ifp->if_mtu) { - error = EMSGSIZE; - goto bad; - } - m->m_flags |= M_BCAST; - } else - m->m_flags &= ~M_BCAST; - -sendit: #if defined(IPFILTER) || defined(IPFILTER_LKM) /* * looks like most checking has been done now...do a filter check |