diff -urNp current/sys/netinet/in_pcb.c limits/sys/netinet/in_pcb.c --- current/sys/netinet/in_pcb.c 2009-06-04 22:03:32.000000000 +0200 +++ limits/sys/netinet/in_pcb.c 2009-06-10 10:22:43.000000000 +0200 @@ -566,6 +566,23 @@ in_pcbladdr(struct inpcb *inp, struct in sin = (struct sockaddr_in *)&sro.ro_dst; sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); + +#ifdef IPSEC + /* + * If there is an IPsec tunnel with a destination matching faddr, + * try to use source address for the tunnel. + */ + sin->sin_addr.s_addr = key_find_src(&faddr->s_addr); + ifa = ifa_ifwithnet(sintosa(sin)); + if (ifa != NULL) { + sin = IA_SIN(ifatoia(ifa)); + if (prison_check_ip4(cred, &sin->sin_addr) == 0) { + laddr->s_addr = sin->sin_addr.s_addr; + goto done; + } + } +#endif + sin->sin_addr.s_addr = faddr->s_addr; /* diff -urNp current/sys/netinet/ip_output.c limits/sys/netinet/ip_output.c --- current/sys/netinet/ip_output.c 2009-06-04 22:03:32.000000000 +0200 +++ limits/sys/netinet/ip_output.c 2009-06-10 10:28:24.000000000 +0200 @@ -80,6 +80,7 @@ __FBSDID("$FreeBSD: src/sys/netinet/ip_o #ifdef IPSEC #include #include +#include #endif /* IPSEC*/ #include @@ -139,6 +140,8 @@ ip_output(struct mbuf *m, struct mbuf *o struct m_tag *fwd_tag = NULL; #endif #ifdef IPSEC + struct sockaddr_in sin; + struct ifaddr *ifa; int no_route_but_check_spd = 0; #endif M_ASSERTPKTHDR(m); @@ -194,6 +197,17 @@ ip_output(struct mbuf *m, struct mbuf *o hlen = ip->ip_hl << 2; } +#ifdef IPSEC + if (ip->ip_src.s_addr == INADDR_ANY) { + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = key_find_src(&ip->ip_dst.s_addr); + ifa = ifa_ifwithnet(sintosa(&sin)); + if (ifa != NULL) + ip->ip_src = IA_SIN(ifatoia(ifa))->sin_addr; + } +#endif + dst = (struct sockaddr_in *)&ro->ro_dst; again: /* diff -urNp current/sys/netipsec/key.c limits/sys/netipsec/key.c --- current/sys/netipsec/key.c 2009-05-29 19:54:25.000000000 +0200 +++ limits/sys/netipsec/key.c 2009-06-10 13:11:17.000000000 +0200 @@ -684,6 +684,40 @@ found: return sp; } +in_addr_t +key_find_src(const in_addr_t *dst) +{ + INIT_VNET_IPSEC(curvnet); + struct secpolicy *sp; + in_addr_t src = INADDR_ANY; + + SPTREE_LOCK(); + LIST_FOREACH(sp, &V_sptree[IPSEC_DIR_OUTBOUND], chain) { + + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + + if (sp->spidx.dir != IPSEC_DIR_OUTBOUND) + continue; + + if (sp->spidx.dst.sa.sa_family != AF_INET) + continue; + + if (key_bbcmp(&sp->spidx.dst.sin.sin_addr.s_addr, dst, sp->spidx.prefd)) + break; + } + + if (sp) { + /* sanity check */ + KEY_CHKSPDIR(sp->spidx.dir, IPSEC_DIR_OUTBOUND, __func__); + + src = sp->spidx.src.sin.sin_addr.s_addr; + } + SPTREE_UNLOCK(); + + return (src); +} + #if 0 /* * return a policy that matches this particular inbound packet. diff -urNp current/sys/netipsec/key.h limits/sys/netipsec/key.h --- current/sys/netipsec/key.h 2009-04-29 09:29:20.000000000 +0200 +++ limits/sys/netipsec/key.h 2009-06-10 09:31:26.000000000 +0200 @@ -104,6 +104,8 @@ extern void key_sa_recordxfer __P((struc extern void key_sa_routechange __P((struct sockaddr *)); extern void key_sa_stir_iv __P((struct secasvar *)); +extern in_addr_t key_find_src(const in_addr_t *dst); + #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPSEC_SA); MALLOC_DECLARE(M_IPSEC_SAH);