diff options
author | Angelos D. Keromytis <angelos@cvs.openbsd.org> | 1999-12-04 23:20:22 +0000 |
---|---|---|
committer | Angelos D. Keromytis <angelos@cvs.openbsd.org> | 1999-12-04 23:20:22 +0000 |
commit | 2b2f59eda8c608d272708986fc8d2c0183eb8dd4 (patch) | |
tree | dbda7dc76dd4d65f22ff652cdc8a1f0f1858f533 /sys/netinet/ip_output.c | |
parent | c4bfe6718e0319e5b13ff515c6cc82e59856a8a1 (diff) |
Address independence, IPv6 support, and the -local flag in ipsecadm is
no longer needed.
Diffstat (limited to 'sys/netinet/ip_output.c')
-rw-r--r-- | sys/netinet/ip_output.c | 567 |
1 files changed, 273 insertions, 294 deletions
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 |