/* * ipsec_common_input() gets called when we receive an IPsec-protected packet * in IPv4 or IPv6. All it does is find the right TDB and call the appropriate * transform. The callback takes care of further processing (like ingress * filtering). */ int ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto, int udpencap) { #define IPSEC_ISTAT(x,y,z) (sproto == IPPROTO_ESP ? (x)++ : \ sproto == IPPROTO_AH ? (y)++ : (z)++) union sockaddr_union dst_address; struct timeval tv; struct tdb *tdbp; struct ifnet *encif; u_int32_t spi; u_int16_t cpi; int s, error; IPSEC_ISTAT(espstat.esps_input, ahstat.ahs_input, ipcompstat.ipcomps_input); if (m == 0) { DPRINTF(("ipsec_common_input(): NULL packet received\n")); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops); return EINVAL; } if ((sproto == IPPROTO_ESP && !esp_enable) || (sproto == IPPROTO_AH && !ah_enable) || #if NPF > 0 (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) || #endif (sproto == IPPROTO_IPCOMP && !ipcomp_enable)) { switch (af) { #ifdef INET case AF_INET: rip_input(m, skip, sproto); break; #endif /* INET */ #ifdef INET6 case AF_INET6: rip6_input(&m, &skip, sproto); break; #endif /* INET6 */ default: DPRINTF(("ipsec_common_input(): unsupported protocol " "family %d\n", af)); m_freem(m); IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf, ipcompstat.ipcomps_nopf); return EPFNOSUPPORT; } return 0; } if ((sproto == IPPROTO_IPCOMP) && (m->m_flags & M_COMP)) { m_freem(m); ipcompstat.ipcomps_pdrops++; DPRINTF(("ipsec_common_input(): repeated decompression\n")); return EINVAL; } if (m->m_pkthdr.len - skip < 2 * sizeof(u_int32_t)) { m_freem(m); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops); DPRINTF(("ipsec_common_input(): packet too small\n")); return EINVAL; } /* Retrieve the SPI from the relevant IPsec header */ if (sproto == IPPROTO_ESP) m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi); else if (sproto == IPPROTO_AH) m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t), (caddr_t) &spi); else if (sproto == IPPROTO_IPCOMP) { m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t), (caddr_t) &cpi); spi = ntohl(htons(cpi)); } /* * Find tunnel control block and (indirectly) call the appropriate * kernel crypto routine. The resulting mbuf chain is a valid * IP packet ready to go through input processing. */ memset(&dst_address, 0, sizeof(dst_address)); dst_address.sa.sa_family = af; switch (af) { #ifdef INET case AF_INET: dst_address.sin.sin_len = sizeof(struct sockaddr_in); m_copydata(m, offsetof(struct ip, ip_dst), sizeof(struct in_addr), (caddr_t) &(dst_address.sin.sin_addr)); break; #endif /* INET */ #ifdef INET6 case AF_INET6: dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6); m_copydata(m, offsetof(struct ip6_hdr, ip6_dst), sizeof(struct in6_addr), (caddr_t) &(dst_address.sin6.sin6_addr)); in6_recoverscope(&dst_address.sin6, &dst_address.sin6.sin6_addr, NULL); break; #endif /* INET6 */ default: DPRINTF(("ipsec_common_input(): unsupported protocol " "family %d\n", af)); m_freem(m); IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf, ipcompstat.ipcomps_nopf); return EPFNOSUPPORT; } s = splsoftnet(); tdbp = gettdb(rtable_l2(m->m_pkthdr.ph_rtableid), spi, &dst_address, sproto); if (tdbp == NULL) { splx(s); DPRINTF(("ipsec_common_input(): could not find SA for " "packet to %s, spi %08x\n", ipsp_address(dst_address), ntohl(spi))); m_freem(m); IPSEC_ISTAT(espstat.esps_notdb, ahstat.ahs_notdb, ipcompstat.ipcomps_notdb); return ENOENT; } if (tdbp->tdb_flags & TDBF_INVALID) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use invalid SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_invalid, ahstat.ahs_invalid, ipcompstat.ipcomps_invalid); return EINVAL; } if (udpencap && !(tdbp->tdb_flags & TDBF_UDPENCAP)) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use non-udpencap SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); espstat.esps_udpinval++; return EINVAL; } if (tdbp->tdb_xform == NULL) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use uninitialized SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_noxform, ahstat.ahs_noxform, ipcompstat.ipcomps_noxform); return ENXIO; } if (sproto != IPPROTO_IPCOMP) { if ((encif = enc_getif(tdbp->tdb_rdomain, tdbp->tdb_tap)) == NULL) { splx(s); DPRINTF(("ipsec_common_input(): " "no enc%u interface for SA %s/%08x/%u\n", tdbp->tdb_tap, ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops, ipcompstat.ipcomps_pdrops); return EACCES; } /* XXX This conflicts with the scoped nature of IPv6 */ m->m_pkthdr.rcvif = encif; } /* Register first use, setup expiration timer. */ if (tdbp->tdb_first_use == 0) { tdbp->tdb_first_use = time_second; tv.tv_usec = 0; tv.tv_sec = tdbp->tdb_exp_first_use + tdbp->tdb_first_use; if (tdbp->tdb_flags & TDBF_FIRSTUSE) timeout_add(&tdbp->tdb_first_tmo, hzto(&tv)); tv.tv_sec = tdbp->tdb_first_use + tdbp->tdb_soft_first_use; if (tdbp->tdb_flags & TDBF_SOFT_FIRSTUSE) timeout_add(&tdbp->tdb_sfirst_tmo, hzto(&tv)); } /* * Call appropriate transform and return -- callback takes care of * everything else. */ error = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff); splx(s); return error; }
void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst; struct rtentry *rt; int error = 0, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ #ifdef IPSEC u_int8_t sproto = 0; struct m_tag *mtag; union sockaddr_union sdst; struct tdb_ident *tdbi; u_int32_t sspi; struct tdb *tdb; int s; #if NPF > 0 struct ifnet *encif; #endif #endif /* IPSEC */ u_int rtableid = 0; /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, m->m_pkthdr.rcvif->if_xname); } 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; #if NPF > 0 reroute: #endif #ifdef IPSEC if (!ipsec_in_use) goto done_spd; s = splnet(); /* * Check if there was an outgoing SA bound to the flow * from a transport protocol. */ /* Do we have any pending SAs to apply ? */ mtag = m_tag_find(m, PACKET_TAG_IPSEC_PENDING_TDB, NULL); if (mtag != NULL) { #ifdef DIAGNOSTIC if (mtag->m_tag_len != sizeof (struct tdb_ident)) panic("ip6_forward: tag of length %d (should be %d", mtag->m_tag_len, sizeof (struct tdb_ident)); #endif tdbi = (struct tdb_ident *)(mtag + 1); tdb = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst, tdbi->proto); if (tdb == NULL) error = -EINVAL; m_tag_delete(m, mtag); } else tdb = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr), &error, IPSP_DIRECTION_OUT, NULL, NULL); if (tdb == NULL) { splx(s); if (error == 0) { /* * No IPsec processing required, we'll just send the * packet out. */ sproto = 0; /* Fall through to routing/multicast handling */ } else { /* * -EINVAL is used to indicate that the packet should * be silently dropped, typically because we've asked * key management for an SA. */ if (error == -EINVAL) /* Should silently drop packet */ error = 0; goto freecopy; } } else { /* Loop detection */ for (mtag = m_tag_first(m); mtag != NULL; mtag = m_tag_next(m, mtag)) { if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE && mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED) continue; tdbi = (struct tdb_ident *)(mtag + 1); if (tdbi->spi == tdb->tdb_spi && tdbi->proto == tdb->tdb_sproto && tdbi->rdomain == tdb->tdb_rdomain && !bcmp(&tdbi->dst, &tdb->tdb_dst, sizeof(union sockaddr_union))) { splx(s); sproto = 0; /* mark as no-IPsec-needed */ goto done_spd; } } /* We need to do IPsec */ bcopy(&tdb->tdb_dst, &sdst, sizeof(sdst)); sspi = tdb->tdb_spi; sproto = tdb->tdb_sproto; splx(s); } /* Fall through to the routing/multicast handling code */ done_spd: #endif /* IPSEC */ #if NPF > 0 rtableid = m->m_pkthdr.rdomain; #endif /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) 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. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); 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 || ip6_forward_rt.ro_tableid != rtableid) { 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 */ ip6_forward_rt.ro_tableid = rtableid; rtalloc_mpath((struct route *)&ip6_forward_rt, &ip6->ip6_src.s6_addr32[0]); } if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } else if (ip6_forward_rt.ro_rt == 0 || (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) || ip6_forward_rt.ro_tableid != rtableid) { 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; ip6_forward_rt.ro_tableid = rtableid; rtalloc_mpath((struct route *)&ip6_forward_rt, &ip6->ip6_src.s6_addr32[0]); if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } rt = ip6_forward_rt.ro_rt; /* * Scope check: if a packet can't be delivered to its destination * for the reason that the destination is beyond the scope of the * source address, discard the packet and return an icmp6 destination * unreachable error with Code 2 (beyond scope of source address). * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] */ if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) != in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, m->m_pkthdr.rcvif->if_xname, rt->rt_ifp->if_xname); } if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); goto freert; } #ifdef IPSEC /* * Check if the packet needs encapsulation. * ipsp_process_packet will never come back to here. * XXX ipsp_process_packet() calls ip6_output(), and there'll be no * PMTU notification. is it okay? */ if (sproto != 0) { s = splnet(); tdb = gettdb(rtable_l2(m->m_pkthdr.rdomain), sspi, &sdst, sproto); if (tdb == NULL) { splx(s); error = EHOSTUNREACH; m_freem(m); goto senderr; /*XXX*/ } #if NPF > 0 if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL || pf_test6(PF_FWD, encif, &m, NULL) != PF_PASS) { splx(s); error = EHOSTUNREACH; m_freem(m); goto senderr; } if (m == NULL) { splx(s); goto senderr; } ip6 = mtod(m, struct ip6_hdr *); /* * PF_TAG_REROUTE handling or not... * Packet is entering IPsec so the routing is * already overruled by the IPsec policy. * Until now the change was not reconsidered. * What's the behaviour? */ #endif m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ /* Callee frees mbuf */ error = ipsp_process_packet(m, tdb, AF_INET6, 0); splx(s); m_freem(mcopy); goto freert; }
/* * ESP output callback, called directly by the crypto driver. */ int esp_output_cb(void *op) { struct cryptop *crp = (struct cryptop *) op; struct tdb_crypto *tc; struct tdb *tdb; struct mbuf *m; int error, s; tc = (struct tdb_crypto *) crp->crp_opaque; m = (struct mbuf *) crp->crp_buf; if (m == NULL) { /* Shouldn't happen... */ free(tc, M_XDATA); crypto_freereq(crp); espstat.esps_crypto++; DPRINTF(("esp_output_cb(): bogus returned buffer from " "crypto\n")); return (EINVAL); } s = spltdb(); tdb = gettdb(tc->tc_spi, &tc->tc_dst, tc->tc_proto); if (tdb == NULL) { free(tc, M_XDATA); espstat.esps_notdb++; DPRINTF(("esp_output_cb(): TDB is expired while in crypto\n")); error = EPERM; goto baddone; } /* Check for crypto errors. */ if (crp->crp_etype) { if (crp->crp_etype == EAGAIN) { /* Reset the session ID */ if (tdb->tdb_cryptoid != 0) tdb->tdb_cryptoid = crp->crp_sid; splx(s); return crypto_dispatch(crp); } free(tc, M_XDATA); espstat.esps_noxform++; DPRINTF(("esp_output_cb(): crypto error %d\n", crp->crp_etype)); error = crp->crp_etype; goto baddone; } free(tc, M_XDATA); /* Release crypto descriptors. */ crypto_freereq(crp); /* * If we're doing half-iv, keep a copy of the last few bytes of the * encrypted part, for use as the next IV. Note that HALF-IV is only * supposed to be used without authentication (the old ESP specs). */ if (tdb->tdb_flags & TDBF_HALFIV) m_copydata(m, m->m_pkthdr.len - tdb->tdb_ivlen, tdb->tdb_ivlen, tdb->tdb_iv); /* Call the IPsec input callback. */ error = ipsp_process_done(m, tdb); splx(s); return error; baddone: splx(s); if (m != NULL) m_freem(m); crypto_freereq(crp); return error; }
/* * ESP input callback, called directly by the crypto driver. */ int esp_input_cb(void *op) { u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN]; int s, hlen, roff, skip, protoff, error; struct mbuf *m1, *mo, *m; struct auth_hash *esph; struct tdb_crypto *tc; struct cryptop *crp; struct m_tag *mtag; struct tdb *tdb; u_int32_t btsx; caddr_t ptr; crp = (struct cryptop *) op; tc = (struct tdb_crypto *) crp->crp_opaque; skip = tc->tc_skip; protoff = tc->tc_protoff; mtag = (struct m_tag *) tc->tc_ptr; m = (struct mbuf *) crp->crp_buf; if (m == NULL) { /* Shouldn't happen... */ free(tc, M_XDATA); crypto_freereq(crp); espstat.esps_crypto++; DPRINTF(("esp_input_cb(): bogus returned buffer from crypto\n")); return (EINVAL); } s = spltdb(); tdb = gettdb(tc->tc_spi, &tc->tc_dst, tc->tc_proto); if (tdb == NULL) { free(tc, M_XDATA); espstat.esps_notdb++; DPRINTF(("esp_input_cb(): TDB is expired while in crypto")); error = EPERM; goto baddone; } esph = (struct auth_hash *) tdb->tdb_authalgxform; /* Check for crypto errors */ if (crp->crp_etype) { if (crp->crp_etype == EAGAIN) { /* Reset the session ID */ if (tdb->tdb_cryptoid != 0) tdb->tdb_cryptoid = crp->crp_sid; splx(s); return crypto_dispatch(crp); } free(tc, M_XDATA); espstat.esps_noxform++; DPRINTF(("esp_input_cb(): crypto error %d\n", crp->crp_etype)); error = crp->crp_etype; goto baddone; } /* If authentication was performed, check now. */ if (esph != NULL) { /* * If we have a tag, it means an IPsec-aware NIC did the verification * for us. */ if (mtag == NULL) { /* Copy the authenticator from the packet */ m_copydata(m, m->m_pkthdr.len - esph->authsize, esph->authsize, aalg); ptr = (caddr_t) (tc + 1); /* Verify authenticator */ if (bcmp(ptr, aalg, esph->authsize)) { free(tc, M_XDATA); DPRINTF(("esp_input_cb(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_badauth++; error = EACCES; goto baddone; } } /* Remove trailing authenticator */ m_adj(m, -(esph->authsize)); } free(tc, M_XDATA); /* Replay window checking, if appropriate */ if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY))) { m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t), (unsigned char *) &btsx); btsx = ntohl(btsx); switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl), tdb->tdb_wnd, &(tdb->tdb_bitmap), 1)) { case 0: /* All's well */ #if NPFSYNC > 0 pfsync_update_tdb(tdb,0); #endif break; case 1: DPRINTF(("esp_input_cb(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_wrap++; error = EACCES; goto baddone; case 2: case 3: DPRINTF(("esp_input_cb(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); error = EACCES; goto baddone; default: DPRINTF(("esp_input_cb(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); espstat.esps_replay++; error = EACCES; goto baddone; } } /* Release the crypto descriptors */ crypto_freereq(crp); /* Determine the ESP header length */ if (tdb->tdb_flags & TDBF_NOREPLAY) hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */ else hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */ /* Find beginning of ESP header */ m1 = m_getptr(m, skip, &roff); if (m1 == NULL) { espstat.esps_hdrops++; splx(s); DPRINTF(("esp_input_cb(): bad mbuf chain, SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); m_freem(m); return EINVAL; } /* Remove the ESP header and IV from the mbuf. */ if (roff == 0) { /* The ESP header was conveniently at the beginning of the mbuf */ m_adj(m1, hlen); if (!(m1->m_flags & M_PKTHDR)) m->m_pkthdr.len -= hlen; } else if (roff + hlen >= m1->m_len) { /* * Part or all of the ESP header is at the end of this mbuf, so * first let's remove the remainder of the ESP header from the * beginning of the remainder of the mbuf chain, if any. */ if (roff + hlen > m1->m_len) { /* Adjust the next mbuf by the remainder */ m_adj(m1->m_next, roff + hlen - m1->m_len); /* The second mbuf is guaranteed not to have a pkthdr... */ m->m_pkthdr.len -= (roff + hlen - m1->m_len); } /* Now, let's unlink the mbuf chain for a second...*/ mo = m1->m_next; m1->m_next = NULL; /* ...and trim the end of the first part of the chain...sick */ m_adj(m1, -(m1->m_len - roff)); if (!(m1->m_flags & M_PKTHDR)) m->m_pkthdr.len -= (m1->m_len - roff); /* Finally, let's relink */ m1->m_next = mo; } else { /* * The ESP header lies in the "middle" of the mbuf...do an * overlapping copy of the remainder of the mbuf over the ESP * header. */ bcopy(mtod(m1, u_char *) + roff + hlen, mtod(m1, u_char *) + roff, m1->m_len - (roff + hlen)); m1->m_len -= hlen; m->m_pkthdr.len -= hlen; } /* Save the last three bytes of decrypted data */ m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree); /* Verify pad length */ if (lastthree[1] + 2 > m->m_pkthdr.len - skip) { espstat.esps_badilen++; splx(s); DPRINTF(("esp_input_cb(): invalid padding length %d for packet in SA %s/%08x\n", lastthree[1], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); m_freem(m); return EINVAL; } /* Verify correct decryption by checking the last padding bytes */ if (!(tdb->tdb_flags & TDBF_RANDOMPADDING)) { if ((lastthree[1] != lastthree[0]) && (lastthree[1] != 0)) { espstat.esps_badenc++; splx(s); DPRINTF(("esp_input(): decryption failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi))); m_freem(m); return EINVAL; } } /* Trim the mbuf chain to remove the trailing authenticator and padding */ m_adj(m, -(lastthree[1] + 2)); /* Restore the Next Protocol field */ m_copyback(m, protoff, sizeof(u_int8_t), lastthree + 2); /* Back to generic IPsec input processing */ error = ipsec_common_input_cb(m, tdb, skip, protoff, mtag); splx(s); return (error); baddone: splx(s); if (m != NULL) m_freem(m); crypto_freereq(crp); return (error); }
/* * IPSEC <> netlink interface * Copyright (C) 1996, 1997 John Ioannidis. * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ char ipsec_netlink_c_version[] = "RCSID $Id: ipsec_netlink.c,v 1.56 2002/01/29 17:17:55 mcr Exp $"; #include <linux/config.h> #include <linux/version.h> #include <linux/kernel.h> /* printk() */ #include "ipsec_param.h" #ifdef MALLOC_SLAB # include <linux/slab.h> /* kmalloc() */ #else /* MALLOC_SLAB */ # include <linux/malloc.h> /* kmalloc() */ #endif /* MALLOC_SLAB */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/interrupt.h> /* mark_bh */ #include <linux/netdevice.h> /* struct device, and other headers */ #include <linux/etherdevice.h> /* eth_type_trans */ #include <linux/ip.h> /* struct iphdr */ #include <linux/skbuff.h> #include <freeswan.h> #ifdef SPINLOCK # ifdef SPINLOCK_23 # include <linux/spinlock.h> /* *lock* */ # else /* 23_SPINLOCK */ # include <asm/spinlock.h> /* *lock* */ # endif /* 23_SPINLOCK */ #endif /* SPINLOCK */ #ifdef NET_21 # include <asm/uaccess.h> # include <linux/in6.h> # define ip_chk_addr inet_addr_type # define IS_MYADDR RTN_LOCAL #endif #include <asm/checksum.h> #include <net/ip.h> #ifdef NETLINK_SOCK # include <linux/netlink.h> #else # include <net/netlink.h> #endif #include "radij.h" #include "ipsec_encap.h" #include "ipsec_radij.h" #include "ipsec_netlink.h" #include "ipsec_xform.h" #include "ipsec_rcv.h" #include "ipsec_ah.h" #include "ipsec_esp.h" #ifdef CONFIG_IPSEC_DEBUG # include "ipsec_tunnel.h" #endif /* CONFIG_IPSEC_DEBUG */ #include <pfkeyv2.h> #include <pfkey.h> #ifdef CONFIG_IPSEC_DEBUG int debug_netlink = 0; #endif /* CONFIG_IPSEC_DEBUG */ #define SENDERR(_x) do { len = -(_x); goto errlab; } while (0) #if 0 int #ifdef NETLINK_SOCK ipsec_callback(int proto, struct sk_buff *skb) #else /* NETLINK_SOCK */ ipsec_callback(struct sk_buff *skb) #endif /* NETLINK_SOCK */ { /* * this happens when we write to /dev/ipsec (c 36 10) */ int len = skb->len; u_char *dat = (u_char *)skb->data; struct encap_msghdr *em = (struct encap_msghdr *)dat; struct tdb *tdbp, *tprev; int i, nspis, error = 0; #ifdef CONFIG_IPSEC_DEBUG struct eroute *eret; char sa[SATOA_BUF]; size_t sa_len; struct sk_buff *first, *last; sa_len = satoa(em->em_said, 0, sa, SATOA_BUF); if(debug_netlink) { printk("klips_debug:ipsec_callback: " "skb=0x%p skblen=%ld em_magic=%d em_type=%d\n", skb, (unsigned long int)skb->len, em->em_magic, em->em_type); switch(em->em_type) { case EMT_SETDEBUG: printk("klips_debug:ipsec_callback: " "set ipsec_debug level\n"); break; case EMT_DELEROUTE: case EMT_CLREROUTE: case EMT_CLRSPIS: break; default: printk("klips_debug:ipsec_callback: " "called for SA:%s\n", sa_len ? sa : " (error)"); } } #endif /* CONFIG_IPSEC_DEBUG */ /* XXXX Temporarily disable netlink I/F code until it gets permanantly ripped out in favour of PF_KEYv2 I/F. */ SENDERR(EPROTONOSUPPORT); /* em = (struct encap_msghdr *)dat; */ if (em->em_magic != EM_MAGIC) { printk("klips_debug:ipsec_callback: " "bad magic=%d failed, should be %d\n", em->em_magic, EM_MAGIC); SENDERR(EINVAL); } switch (em->em_type) { case EMT_SETDEBUG: #ifdef CONFIG_IPSEC_DEBUG if(em->em_db_nl >> (sizeof(em->em_db_nl) * 8 - 1)) { em->em_db_nl &= ~(1 << (sizeof(em->em_db_nl) * 8 -1)); debug_tunnel |= em->em_db_tn; debug_netlink |= em->em_db_nl; debug_xform |= em->em_db_xf; debug_eroute |= em->em_db_er; debug_spi |= em->em_db_sp; debug_radij |= em->em_db_rj; debug_esp |= em->em_db_es; debug_ah |= em->em_db_ah; debug_rcv |= em->em_db_rx; debug_pfkey |= em->em_db_ky; if(debug_netlink) printk("klips_debug:ipsec_callback: set\n"); } else { if(debug_netlink) printk("klips_debug:ipsec_callback: unset\n"); debug_tunnel &= em->em_db_tn; debug_netlink &= em->em_db_nl; debug_xform &= em->em_db_xf; debug_eroute &= em->em_db_er; debug_spi &= em->em_db_sp; debug_radij &= em->em_db_rj; debug_esp &= em->em_db_es; debug_ah &= em->em_db_ah; debug_rcv &= em->em_db_rx; debug_pfkey &= em->em_db_ky; } #else /* CONFIG_IPSEC_DEBUG */ printk("klips_debug:ipsec_callback: " "debugging not enabled\n"); SENDERR(EINVAL); #endif /* CONFIG_IPSEC_DEBUG */ break; case EMT_SETEROUTE: if ((error = ipsec_makeroute(&(em->em_eaddr), &(em->em_emask), em->em_ersaid, 0, NULL, NULL, NULL))) SENDERR(-error); break; case EMT_REPLACEROUTE: if ((error = ipsec_breakroute(&(em->em_eaddr), &(em->em_emask), &first, &last)) == EINVAL) { kfree_skb(first); kfree_skb(last); SENDERR(-error); } if ((error = ipsec_makeroute(&(em->em_eaddr), &(em->em_emask), em->em_ersaid, NULL, NULL))) SENDERR(-error); break; case EMT_DELEROUTE: if ((error = ipsec_breakroute(&(em->em_eaddr), &(em->em_emask), &first, &last))) kfree_skb(first); kfree_skb(last); SENDERR(-error); break; case EMT_CLREROUTE: if ((error = ipsec_cleareroutes())) SENDERR(-error); break; case EMT_SETSPI: if (em->em_if >= 5) /* XXX -- why 5? */ SENDERR(ENODEV); tdbp = gettdb(&(em->em_said)); if (tdbp == NULL) { tdbp = (struct tdb *)kmalloc(sizeof (*tdbp), GFP_ATOMIC); if (tdbp == NULL) SENDERR(ENOBUFS); memset((caddr_t)tdbp, 0, sizeof(*tdbp)); tdbp->tdb_said = em->em_said; tdbp->tdb_flags = em->em_flags; if(ip_chk_addr((unsigned long)em->em_said.dst.s_addr) == IS_MYADDR) { tdbp->tdb_flags |= EMT_INBOUND; } KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "existing Tunnel Descriptor Block not found (this is good) for SA: %s, %s-bound, allocating.\n", sa_len ? sa : " (error)", (tdbp->tdb_flags & EMT_INBOUND) ? "in" : "out"); /* XXX tdbp->tdb_rcvif = &(enc_softc[em->em_if].enc_if);*/ tdbp->tdb_rcvif = NULL; } else { KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_SETSPI found an old Tunnel Descriptor Block for SA: %s, delete it first.\n", sa_len ? sa : " (error)"); SENDERR(EEXIST); } if ((error = tdb_init(tdbp, em))) { KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_SETSPI not successful for SA: %s, deleting.\n", sa_len ? sa : " (error)"); ipsec_tdbwipe(tdbp); SENDERR(-error); } tdbp->tdb_lifetime_addtime_c = jiffies/HZ; tdbp->tdb_state = 1; if(!tdbp->tdb_lifetime_allocations_c) { tdbp->tdb_lifetime_allocations_c += 1; } puttdb(tdbp); KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_SETSPI successful for SA: %s\n", sa_len ? sa : " (error)"); break; case EMT_DELSPI: if (em->em_if >= 5) /* XXX -- why 5? */ SENDERR(ENODEV); spin_lock_bh(&tdb_lock); tdbp = gettdb(&(em->em_said)); if (tdbp == NULL) { KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_DELSPI Tunnel Descriptor Block not found for SA%s, could not delete.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(ENXIO); /* XXX -- wrong error message... */ } else { if((error = deltdbchain(tdbp))) { spin_unlock_bh(&tdb_lock); SENDERR(-error); } } spin_unlock_bh(&tdb_lock); break; case EMT_GRPSPIS: nspis = (len - EMT_GRPSPIS_FLEN) / sizeof(em->em_rel[0]); if ((nspis * (sizeof(em->em_rel[0]))) != (len - EMT_GRPSPIS_FLEN)) { printk("klips_debug:ipsec_callback: " "EMT_GRPSPI message size incorrect, expected nspis(%d)*%d, got %d.\n", nspis, sizeof(em->em_rel[0]), (len - EMT_GRPSPIS_FLEN)); SENDERR(EINVAL); break; } spin_lock_bh(&tdb_lock); for (i = 0; i < nspis; i++) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_GRPSPI for SA(%d) %s,\n", i, sa_len ? sa : " (error)"); if ((tdbp = gettdb(&(em->em_rel[i].emr_said))) == NULL) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_GRPSPI Tunnel Descriptor Block not found for SA%s, could not group.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(ENXIO); } else { if(tdbp->tdb_inext || tdbp->tdb_onext) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_GRPSPI Tunnel Descriptor Block already grouped for SA: %s, can't regroup.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(EBUSY); } em->em_rel[i].emr_tdb = tdbp; } } tprev = em->em_rel[0].emr_tdb; tprev->tdb_inext = NULL; for (i = 1; i < nspis; i++) { tdbp = em->em_rel[i].emr_tdb; tprev->tdb_onext = tdbp; tdbp->tdb_inext = tprev; tprev = tdbp; } tprev->tdb_onext = NULL; spin_unlock_bh(&tdb_lock); error = 0; break; case EMT_UNGRPSPIS: if (len != (8 + (sizeof(struct sa_id) + sizeof(struct tdb *)) /* 12 */) ) { printk("klips_debug:ipsec_callback: " "EMT_UNGRPSPIS message size incorrect, expected %d, got %d.\n", 8 + (sizeof(struct sa_id) + sizeof(struct tdb *)), len); SENDERR(EINVAL); break; } spin_lock_bh(&tdb_lock); if ((tdbp = gettdb(&(em->em_rel[0].emr_said))) == NULL) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_UGRPSPI Tunnel Descriptor Block not found for SA%s, could not ungroup.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(ENXIO); } while(tdbp->tdb_onext) { tdbp = tdbp->tdb_onext; } while(tdbp->tdb_inext) { tprev = tdbp; tdbp = tdbp->tdb_inext; tprev->tdb_inext = NULL; tdbp->tdb_onext = NULL; } spin_unlock_bh(&tdb_lock); break; case EMT_CLRSPIS: KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "spi clear called.\n"); if (em->em_if >= 5) /* XXX -- why 5? */ SENDERR(ENODEV); ipsec_tdbcleanup(0); break; default: KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "unknown message type\n"); SENDERR(EINVAL); }