#include <kern/locks.h>
#include <sys/sysctl.h>
+#include <machine/endian.h>
+
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/ip_dummynet.h>
#endif
+#if PF
+#include <net/pfvar.h>
+#endif /* PF */
+
#if IPFIREWALL_FORWARD_DEBUG
#define print_ip(a) printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\
(ntohl(a.s_addr)>>16)&0xFF,\
extern int (*fr_checkp)(struct ip *, int, struct ifnet *, int, struct mbuf **);
-extern u_long route_generation;
-
extern struct protosw inetsw[];
extern struct ip_linklocal_stat ip_linklocal_stat;
struct ip_out_args *ipoa
)
{
- struct ip *ip, *mhip;
+ struct ip *ip;
struct ifnet *ifp = NULL;
- struct mbuf *m = m0;
+ struct mbuf *m = m0, **mppn = NULL;
int hlen = sizeof (struct ip);
int len = 0, off, error = 0;
struct sockaddr_in *dst = NULL;
- struct in_ifaddr *ia = NULL;
+ struct in_ifaddr *ia = NULL, *src_ia = NULL;
int isbroadcast, sw_csum;
struct in_addr pkt_dst;
#if IPSEC
struct route saved_route;
struct ip_out_args saved_ipoa;
struct mbuf * packetlist;
- int pktcnt = 0;
+ int pktcnt = 0, tso = 0;
unsigned int ifscope;
boolean_t select_srcif;
args.rule = NULL;
args.divert_rule = 0; /* divert cookie */
args.ipoa = NULL;
-
+
+ if (SLIST_EMPTY(&m0->m_pkthdr.tags))
+ goto ipfw_tags_done;
+
/* Grab info from mtags prepended to the chain */
#if DUMMYNET
- if ((tag = m_tag_locate(m0, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_DUMMYNET, NULL)) != NULL) {
+ if ((tag = m_tag_locate(m0, KERNEL_MODULE_TAG_ID,
+ KERNEL_TAG_TYPE_DUMMYNET, NULL)) != NULL) {
struct dn_pkt_tag *dn_tag;
-
+
dn_tag = (struct dn_pkt_tag *)(tag+1);
args.rule = dn_tag->rule;
opt = NULL;
saved_route = dn_tag->ro;
ro = &saved_route;
-
+
imo = NULL;
dst = dn_tag->dn_dst;
ifp = dn_tag->ifp;
flags = dn_tag->flags;
saved_ipoa = dn_tag->ipoa;
ipoa = &saved_ipoa;
-
+
m_tag_delete(m0, tag);
}
#endif /* DUMMYNET */
#if IPDIVERT
- if ((tag = m_tag_locate(m0, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_DIVERT, NULL)) != NULL) {
+ if ((tag = m_tag_locate(m0, KERNEL_MODULE_TAG_ID,
+ KERNEL_TAG_TYPE_DIVERT, NULL)) != NULL) {
struct divert_tag *div_tag;
-
+
div_tag = (struct divert_tag *)(tag+1);
args.divert_rule = div_tag->cookie;
}
#endif /* IPDIVERT */
- if ((tag = m_tag_locate(m0, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_IPFORWARD, NULL)) != NULL) {
+ if ((tag = m_tag_locate(m0, KERNEL_MODULE_TAG_ID,
+ KERNEL_TAG_TYPE_IPFORWARD, NULL)) != NULL) {
struct ip_fwd_tag *ipfwd_tag;
-
+
ipfwd_tag = (struct ip_fwd_tag *)(tag+1);
args.next_hop = ipfwd_tag->next_hop;
-
+
m_tag_delete(m0, tag);
}
+ipfw_tags_done:
#endif /* IPFIREWALL */
m = m0;
-
+
#if DIAGNOSTIC
if ( !m || (m->m_flags & M_PKTHDR) != 0)
panic("ip_output no HDR");
#endif
/*
- * Do not perform source interface selection when forwarding.
* At present the IP_OUTARGS flag implies a request for IP to
- * perform source interface selection.
+ * perform source interface selection. In the forwarding case,
+ * only the ifscope value is used, as source interface selection
+ * doesn't take place.
*/
- if (ip_doscopedroute &&
- (flags & (IP_OUTARGS | IP_FORWARDING)) == IP_OUTARGS) {
- select_srcif = TRUE;
+ if (ip_doscopedroute && (flags & IP_OUTARGS)) {
+ select_srcif = !(flags & IP_FORWARDING);
ifscope = ipoa->ipoa_ifscope;
} else {
select_srcif = FALSE;
#if IPFIREWALL
if (args.rule != NULL) { /* dummynet already saw us */
- ip = mtod(m, struct ip *);
- hlen = IP_VHL_HL(ip->ip_vhl) << 2 ;
- lck_mtx_lock(rt_mtx);
- if (ro->ro_rt != NULL)
- ia = (struct in_ifaddr *)ro->ro_rt->rt_ifa;
- if (ia)
- ifaref(&ia->ia_ifa);
- lck_mtx_unlock(rt_mtx);
+ ip = mtod(m, struct ip *);
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2 ;
+ if (ro->ro_rt != NULL) {
+ RT_LOCK_SPIN(ro->ro_rt);
+ ia = (struct in_ifaddr *)ro->ro_rt->rt_ifa;
+ if (ia)
+ ifaref(&ia->ia_ifa);
+ RT_UNLOCK(ro->ro_rt);
+ }
#if IPSEC
- if (ipsec_bypass == 0 && (flags & IP_NOIPSEC) == 0) {
- so = ipsec_getsocket(m);
- (void)ipsec_setsocket(m, NULL);
+ if (ipsec_bypass == 0 && (flags & IP_NOIPSEC) == 0) {
+ so = ipsec_getsocket(m);
+ (void)ipsec_setsocket(m, NULL);
}
#endif
- goto sendit;
+ goto sendit;
}
#endif /* IPFIREWALL */
* No need to proccess packet twice if we've
* already seen it
*/
- inject_filter_ref = ipf_get_inject_filter(m);
+ if (!SLIST_EMPTY(&m->m_pkthdr.tags))
+ inject_filter_ref = ipf_get_inject_filter(m);
+ else
+ inject_filter_ref = 0;
if (opt) {
m = ip_insertoptions(m, opt, &len);
#else
ip->ip_id = htons(ip_id++);
#endif
- OSAddAtomic(1, (SInt32*)&ipstat.ips_localout);
+ OSAddAtomic(1, &ipstat.ips_localout);
} else {
hlen = IP_VHL_HL(ip->ip_vhl) << 2;
}
* cache with IPv6.
*/
- lck_mtx_lock(rt_mtx);
if (ro->ro_rt != NULL) {
if (ro->ro_rt->generation_id != route_generation &&
((flags & (IP_ROUTETOIF | IP_FORWARDING)) == 0) &&
- (ip->ip_src.s_addr != INADDR_ANY) &&
- (ifa_foraddr(ip->ip_src.s_addr) == 0)) {
- error = EADDRNOTAVAIL;
- lck_mtx_unlock(rt_mtx);
- goto bad;
+ (ip->ip_src.s_addr != INADDR_ANY)) {
+ src_ia = ifa_foraddr(ip->ip_src.s_addr);
+ if (src_ia == NULL) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ ifafree(&src_ia->ia_ifa);
}
+ /*
+ * Test rt_flags without holding rt_lock for performance
+ * reasons; if the route is down it will hopefully be
+ * caught by the layer below (since it uses this route
+ * as a hint) or during the next transmit.
+ */
if ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
dst->sin_family != AF_INET ||
dst->sin_addr.s_addr != pkt_dst.s_addr) {
- rtfree_locked(ro->ro_rt);
+ rtfree(ro->ro_rt);
ro->ro_rt = NULL;
}
/*
ifafree(&ia->ia_ifa);
if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0) {
if ((ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) {
- OSAddAtomic(1, (SInt32*)&ipstat.ips_noroute);
+ OSAddAtomic(1, &ipstat.ips_noroute);
error = ENETUNREACH;
- lck_mtx_unlock(rt_mtx);
goto bad;
}
}
if (ia != NULL)
ifafree(&ia->ia_ifa);
- /* Could use IFP_TO_IA instead but rt_mtx is already held */
- for (ia = TAILQ_FIRST(&in_ifaddrhead);
- ia != NULL && ia->ia_ifp != ifp;
- ia = TAILQ_NEXT(ia, ia_link))
- continue;
-
- if (ia != NULL)
- ifaref(&ia->ia_ifa);
+ /* Macro takes reference on ia */
+ IFP_TO_IA(ifp, ia);
} else {
boolean_t cloneok = FALSE;
/*
* route (for this PCB instance) before.
*/
if (select_srcif && ip->ip_src.s_addr != INADDR_ANY &&
- (ro->ro_rt == NULL ||
+ (ro->ro_rt == NULL || !(ro->ro_rt->rt_flags & RTF_UP) ||
ro->ro_rt->generation_id != route_generation ||
!(ro->ro_flags & ROF_SRCIF_SELECTED))) {
struct ifaddr *ifa;
if (ifa == NULL && !(flags & IP_RAWOUTPUT) &&
ifscope != lo_ifp->if_index) {
error = EADDRNOTAVAIL;
- lck_mtx_unlock(rt_mtx);
goto bad;
}
if (cloneok || dst->sin_addr.s_addr == INADDR_BROADCAST)
ign &= ~RTF_PRCLONING;
- rtalloc_scoped_ign_locked(ro, ign, ifscope);
+ /*
+ * Loosen the route lookup criteria if the ifscope
+ * corresponds to the loopback interface; this is
+ * needed to support Application Layer Gateways
+ * listening on loopback, in conjunction with packet
+ * filter redirection rules. The final source IP
+ * address will be rewritten by the packet filter
+ * prior to the RFC1122 loopback check below.
+ */
+ if (ifscope == lo_ifp->if_index)
+ rtalloc_ign(ro, ign);
+ else
+ rtalloc_scoped_ign(ro, ign, ifscope);
}
if (ro->ro_rt == NULL) {
- OSAddAtomic(1, (SInt32*)&ipstat.ips_noroute);
+ OSAddAtomic(1, &ipstat.ips_noroute);
error = EHOSTUNREACH;
- lck_mtx_unlock(rt_mtx);
goto bad;
}
if (ia)
ifafree(&ia->ia_ifa);
+ RT_LOCK_SPIN(ro->ro_rt);
ia = ifatoia(ro->ro_rt->rt_ifa);
if (ia)
ifaref(&ia->ia_ifa);
ro->ro_rt->rt_use++;
if (ro->ro_rt->rt_flags & RTF_GATEWAY)
dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
- if (ro->ro_rt->rt_flags & RTF_HOST)
+ if (ro->ro_rt->rt_flags & RTF_HOST) {
isbroadcast = (ro->ro_rt->rt_flags & RTF_BROADCAST);
- else
+ } else {
+ /* Become a regular mutex */
+ RT_CONVERT_LOCK(ro->ro_rt);
isbroadcast = in_broadcast(dst->sin_addr, ifp);
+ }
+ RT_UNLOCK(ro->ro_rt);
}
- lck_mtx_unlock(rt_mtx);
+
if (IN_MULTICAST(ntohl(pkt_dst.s_addr))) {
struct in_multi *inm;
*/
if ((imo == NULL) || (imo->imo_multicast_vif == -1)) {
if ((ifp->if_flags & IFF_MULTICAST) == 0) {
- OSAddAtomic(1, (SInt32*)&ipstat.ips_noroute);
+ OSAddAtomic(1, &ipstat.ips_noroute);
error = ENETUNREACH;
goto bad;
}
* of outgoing interface.
*/
if (ip->ip_src.s_addr == INADDR_ANY) {
- register struct in_ifaddr *ia1;
- lck_mtx_lock(rt_mtx);
+ struct in_ifaddr *ia1;
+ lck_rw_lock_shared(in_ifaddr_rwlock);
TAILQ_FOREACH(ia1, &in_ifaddrhead, ia_link)
if (ia1->ia_ifp == ifp) {
ip->ip_src = IA_SIN(ia1)->sin_addr;
-
break;
}
- lck_mtx_unlock(rt_mtx);
+ lck_rw_done(in_ifaddr_rwlock);
if (ip->ip_src.s_addr == INADDR_ANY) {
error = ENETUNREACH;
goto bad;
ipf_ref();
/* 4135317 - always pass network byte order to filter */
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
-
+#endif
+
TAILQ_FOREACH(filter, &ipv4_filters, ipf_link) {
if (seen == 0) {
if ((struct ipfilter *)inject_filter_ref == filter)
/* set back to host byte order */
ip = mtod(m, struct ip *);
+
+#if BYTE_ORDER != BIG_ENDIAN
NTOHS(ip->ip_len);
NTOHS(ip->ip_off);
-
+#endif
+
ipf_unref();
didfilter = 1;
}
}
sendit:
+#if PF
+ /* Invoke outbound packet filter */
+ if (pf_af_hook(ifp, mppn, &m, AF_INET, FALSE) != 0) {
+ if (packetlist == m0) {
+ packetlist = m;
+ mppn = NULL;
+ }
+ if (m != NULL) {
+ m0 = m;
+ /* Next packet in the chain */
+ goto loopit;
+ } else if (packetlist != NULL) {
+ /* No more packet; send down the chain */
+ goto sendchain;
+ }
+ /* Nothing left; we're done */
+ goto done;
+ }
+ m0 = m;
+ ip = mtod(m, struct ip *);
+ pkt_dst = ip->ip_dst;
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#endif /* PF */
/*
* Force IP TTL to 255 following draft-ietf-zeroconf-ipv4-linklocal.txt
*/
if (!didfilter && !TAILQ_EMPTY(&ipv4_filters)) {
struct ipfilter *filter;
int seen = (inject_filter_ref == 0);
-
+
+ /* Check that a TSO frame isn't passed to a filter.
+ * This could happen if a filter is inserted while
+ * TCP is sending the TSO packet.
+ */
+ if (m->m_pkthdr.csum_flags & CSUM_TSO_IPV4) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+
ipf_ref();
/* 4135317 - always pass network byte order to filter */
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
-
+#endif
+
TAILQ_FOREACH(filter, &ipv4_filters, ipf_link) {
if (seen == 0) {
if ((struct ipfilter *)inject_filter_ref == filter)
/* set back to host byte order */
ip = mtod(m, struct ip *);
+
+#if BYTE_ORDER != BIG_ENDIAN
NTOHS(ip->ip_len);
NTOHS(ip->ip_off);
-
+#endif
+
ipf_unref();
}
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
+#endif
error = ipsec4_output(&state, sp, flags);
hlen = ip->ip_hl << 2;
#endif
/* Check that there wasn't a route change and src is still valid */
-
- lck_mtx_lock(rt_mtx);
- if (ro->ro_rt && ro->ro_rt->generation_id != route_generation) {
- if (ifa_foraddr(ip->ip_src.s_addr) == 0 && ((flags & (IP_ROUTETOIF | IP_FORWARDING)) == 0)) {
- error = EADDRNOTAVAIL;
- lck_mtx_unlock(rt_mtx);
- KERNEL_DEBUG(DBG_FNC_IPSEC4_OUTPUT | DBG_FUNC_END, 5,0,0,0,0);
+ if (ro->ro_rt != NULL && ro->ro_rt->generation_id != route_generation) {
+ if ((src_ia = ifa_foraddr(ip->ip_src.s_addr)) == NULL &&
+ ((flags & (IP_ROUTETOIF | IP_FORWARDING)) == 0)) {
+ error = EADDRNOTAVAIL;
+ KERNEL_DEBUG(DBG_FNC_IPSEC4_OUTPUT | DBG_FUNC_END,
+ 5,0,0,0,0);
goto bad;
}
- rtfree_locked(ro->ro_rt);
+ rtfree(ro->ro_rt);
ro->ro_rt = NULL;
+ if (src_ia != NULL)
+ ifafree(&src_ia->ia_ifa);
}
if (ro->ro_rt == NULL) {
if ((flags & IP_ROUTETOIF) == 0) {
- printf("ip_output: "
- "can't update route after IPsec processing\n");
- error = EHOSTUNREACH; /*XXX*/
- lck_mtx_unlock(rt_mtx);
- KERNEL_DEBUG(DBG_FNC_IPSEC4_OUTPUT | DBG_FUNC_END, 6,0,0,0,0);
+ printf("ip_output: can't update route after "
+ "IPsec processing\n");
+ error = EHOSTUNREACH; /*XXX*/
+ KERNEL_DEBUG(DBG_FNC_IPSEC4_OUTPUT | DBG_FUNC_END,
+ 6,0,0,0,0);
goto bad;
}
} else {
if (ia)
ifafree(&ia->ia_ifa);
+ RT_LOCK_SPIN(ro->ro_rt);
ia = ifatoia(ro->ro_rt->rt_ifa);
if (ia)
ifaref(&ia->ia_ifa);
ifp = ro->ro_rt->rt_ifp;
+ RT_UNLOCK(ro->ro_rt);
}
- lck_mtx_unlock(rt_mtx);
/* make it flipped, again. */
+
+#if BYTE_ORDER != BIG_ENDIAN
NTOHS(ip->ip_len);
NTOHS(ip->ip_off);
+#endif
+
KERNEL_DEBUG(DBG_FNC_IPSEC4_OUTPUT | DBG_FUNC_END, 7,0xff,0xff,0xff,0xff);
/* Pass to filters again */
if (!TAILQ_EMPTY(&ipv4_filters)) {
struct ipfilter *filter;
+ /* Check that a TSO frame isn't passed to a filter.
+ * This could happen if a filter is inserted while
+ * TCP is sending the TSO packet.
+ */
+ if (m->m_pkthdr.csum_flags & CSUM_TSO_IPV4) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+
ipf_ref();
/* 4135317 - always pass network byte order to filter */
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
-
+#endif
+
TAILQ_FOREACH(filter, &ipv4_filters, ipf_link) {
if (filter->ipf_filter.ipf_output) {
errno_t result;
/* set back to host byte order */
ip = mtod(m, struct ip *);
+
+#if BYTE_ORDER != BIG_ENDIAN
NTOHS(ip->ip_len);
NTOHS(ip->ip_off);
-
+#endif
+
ipf_unref();
}
skip_ipsec:
}
/* Restore packet header fields to original values */
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
+#endif
/* Deliver packet to divert input routine */
divert_packet(m, 0, off & 0xffff, args.divert_rule);
* as the packet runs through ip_input() as
* it is done through a ISR.
*/
+ lck_rw_lock_shared(in_ifaddr_rwlock);
TAILQ_FOREACH(ia_fw, &in_ifaddrhead, ia_link) {
/*
* If the addr to forward to is one
dst->sin_addr.s_addr)
break;
}
- if (ia) {
+ lck_rw_done(in_ifaddr_rwlock);
+ if (ia_fw) {
/* tell ip_input "dont filter" */
struct m_tag *fwd_tag;
struct ip_fwd_tag *ipfwd_tag;
- fwd_tag = m_tag_alloc(KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_IPFORWARD,
- sizeof(struct sockaddr_in), M_NOWAIT);
+ fwd_tag = m_tag_alloc(KERNEL_MODULE_TAG_ID,
+ KERNEL_TAG_TYPE_IPFORWARD,
+ sizeof (*ipfwd_tag), M_NOWAIT);
if (fwd_tag == NULL) {
error = ENOBUFS;
goto bad;
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
ip->ip_sum = in_cksum(m, hlen);
}
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
-
+#endif
/* we need to call dlil_output to run filters
* and resync to avoid recursion loops.
*/
bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst));
- ro_fwd->ro_rt = 0;
- lck_mtx_lock(rt_mtx);
- rtalloc_ign_locked(ro_fwd, RTF_PRCLONING);
+ ro_fwd->ro_rt = NULL;
+ rtalloc_ign(ro_fwd, RTF_PRCLONING);
- if (ro_fwd->ro_rt == 0) {
- OSAddAtomic(1, (SInt32*)&ipstat.ips_noroute);
+ if (ro_fwd->ro_rt == NULL) {
+ OSAddAtomic(1, &ipstat.ips_noroute);
error = EHOSTUNREACH;
- lck_mtx_unlock(rt_mtx);
goto bad;
}
+ RT_LOCK_SPIN(ro_fwd->ro_rt);
ia_fw = ifatoia(ro_fwd->ro_rt->rt_ifa);
+ if (ia_fw != NULL)
+ ifaref(&ia_fw->ia_ifa);
ifp = ro_fwd->ro_rt->rt_ifp;
ro_fwd->ro_rt->rt_use++;
if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY)
dst = (struct sockaddr_in *)ro_fwd->ro_rt->rt_gateway;
- if (ro_fwd->ro_rt->rt_flags & RTF_HOST)
+ if (ro_fwd->ro_rt->rt_flags & RTF_HOST) {
isbroadcast =
(ro_fwd->ro_rt->rt_flags & RTF_BROADCAST);
- else
+ } else {
+ /* Become a regular mutex */
+ RT_CONVERT_LOCK(ro_fwd->ro_rt);
isbroadcast = in_broadcast(dst->sin_addr, ifp);
- rtfree_locked(ro->ro_rt);
+ }
+ RT_UNLOCK(ro_fwd->ro_rt);
+ rtfree(ro->ro_rt);
ro->ro_rt = ro_fwd->ro_rt;
dst = (struct sockaddr_in *)&ro_fwd->ro_dst;
- lck_mtx_unlock(rt_mtx);
/*
* If we added a default src ip earlier,
* which would have been gotten from the-then
* interface, do it again, from the new one.
*/
- if (fwd_rewrite_src)
- ip->ip_src = IA_SIN(ia_fw)->sin_addr;
+ if (ia_fw != NULL) {
+ if (fwd_rewrite_src)
+ ip->ip_src = IA_SIN(ia_fw)->sin_addr;
+ ifafree(&ia_fw->ia_ifa);
+ }
goto pass ;
}
#endif /* IPFIREWALL_FORWARD */
if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
((ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
(ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) {
- OSAddAtomic(1, (SInt32*)&ipstat.ips_badaddr);
+ OSAddAtomic(1, &ipstat.ips_badaddr);
m_freem(m);
/*
* Do not simply drop the packet just like a firewall -- we want the
}
#endif
m->m_pkthdr.csum_flags |= CSUM_IP;
+ tso = (ifp->if_hwassist & IFNET_TSO_IPV4) && (m->m_pkthdr.csum_flags & CSUM_TSO_IPV4);
+
sw_csum = m->m_pkthdr.csum_flags
& ~IF_HWASSIST_CSUM_FLAGS(ifp->if_hwassist);
* If small enough for interface, or the interface will take
* care of the fragmentation for us, can just send directly.
*/
- if ((u_short)ip->ip_len <= ifp->if_mtu ||
+ if ((u_short)ip->ip_len <= ifp->if_mtu || tso ||
ifp->if_hwassist & CSUM_FRAGMENT) {
- struct rtentry *rte;
+ if (tso)
+ m->m_pkthdr.csum_flags |= CSUM_TSO_IPV4;
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
+#endif
+
ip->ip_sum = 0;
if (sw_csum & CSUM_DELAY_IP) {
ip->ip_sum = in_cksum(m, hlen);
ipsec_delaux(m);
#endif
if (packetchain == 0) {
- lck_mtx_lock(rt_mtx);
- if ((rte = ro->ro_rt) != NULL)
- rtref(rte);
- lck_mtx_unlock(rt_mtx);
- error = ifnet_output(ifp, PF_INET, m, rte,
+ error = ifnet_output(ifp, PF_INET, m, ro->ro_rt,
(struct sockaddr *)dst);
- if (rte != NULL)
- rtfree(rte);
goto done;
}
else { /* packet chaining allows us to reuse the route for all packets */
+ mppn = &m->m_nextpkt;
m = m->m_nextpkt;
if (m == NULL) {
+#if PF
+sendchain:
+#endif /* PF */
if (pktcnt > ip_maxchainsent)
ip_maxchainsent = pktcnt;
- lck_mtx_lock(rt_mtx);
- if ((rte = ro->ro_rt) != NULL)
- rtref(rte);
- lck_mtx_unlock(rt_mtx);
//send
error = ifnet_output(ifp, PF_INET, packetlist,
- rte, (struct sockaddr *)dst);
- if (rte != NULL)
- rtfree(rte);
+ ro->ro_rt, (struct sockaddr *)dst);
pktcnt = 0;
goto done;
* Too large for interface; fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
- if (ip->ip_off & IP_DF) {
+
+ if (ip->ip_off & IP_DF || (m->m_pkthdr.csum_flags & CSUM_TSO_IPV4)) {
error = EMSGSIZE;
/*
* This case can happen if the user changed the MTU
+ *
* of an interface after enabling IP on it. Because
* most netifs don't keep track of routes pointing to
* them, there is no way for one to update all its
* routes when the MTU is changed.
*/
-
- lck_mtx_lock(rt_mtx);
+ RT_LOCK_SPIN(ro->ro_rt);
if (ro->ro_rt && (ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST))
&& !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU)
&& (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) {
ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu;
}
- lck_mtx_unlock(rt_mtx);
- OSAddAtomic(1, (SInt32*)&ipstat.ips_cantfrag);
+ RT_UNLOCK(ro->ro_rt);
+ OSAddAtomic(1, &ipstat.ips_cantfrag);
goto bad;
}
- len = (ifp->if_mtu - hlen) &~ 7;
- if (len < 8) {
- error = EMSGSIZE;
+
+ error = ip_fragment(m, ifp, ifp->if_mtu, sw_csum);
+ if (error != 0) {
+ m0 = m = NULL;
goto bad;
}
+ KERNEL_DEBUG(DBG_LAYER_END, ip->ip_dst.s_addr,
+ ip->ip_src.s_addr, ip->ip_p, ip->ip_off, ip->ip_len);
+
+ for (m = m0; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = 0;
+#if IPSEC
+ /* clean ipsec history once it goes out of the node */
+ if (ipsec_bypass == 0 && (flags & IP_NOIPSEC) == 0)
+ ipsec_delaux(m);
+#endif
+ if (error == 0) {
+#ifndef __APPLE__
+ /* Record statistics for this interface address. */
+ if (ia != NULL) {
+ ia->ia_ifa.if_opackets++;
+ ia->ia_ifa.if_obytes += m->m_pkthdr.len;
+ }
+#endif
+ if ((packetchain != 0) && (pktcnt > 0))
+ panic("ip_output: mix of packet in packetlist is wrong=%p", packetlist);
+ error = ifnet_output(ifp, PF_INET, m, ro->ro_rt,
+ (struct sockaddr *)dst);
+ } else
+ m_freem(m);
+ }
+
+ if (error == 0)
+ OSAddAtomic(1, &ipstat.ips_fragmented);
+
+done:
+ if (ia) {
+ ifafree(&ia->ia_ifa);
+ ia = NULL;
+ }
+#if IPSEC
+ if (ipsec_bypass == 0 && (flags & IP_NOIPSEC) == 0) {
+ if (ro == &iproute && ro->ro_rt) {
+ rtfree(ro->ro_rt);
+ ro->ro_rt = NULL;
+ }
+ if (sp != NULL) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ip_output call free SP:%x\n", sp));
+ key_freesp(sp, KEY_SADB_UNLOCKED);
+ }
+ }
+#endif /* IPSEC */
+
+ KERNEL_DEBUG(DBG_FNC_IP_OUTPUT | DBG_FUNC_END, error,0,0,0,0);
+ return (error);
+bad:
+ m_freem(m0);
+ goto done;
+}
+
+int
+ip_fragment(struct mbuf *m, struct ifnet *ifp, unsigned long mtu, int sw_csum)
+{
+ struct ip *ip, *mhip;
+ int len, hlen, mhlen, firstlen, off, error = 0;
+ struct mbuf **mnext = &m->m_nextpkt, *m0;
+ int nfrags = 1;
+
+ ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+
+ firstlen = len = (mtu - hlen) &~ 7;
+ if (len < 8) {
+ m_freem(m);
+ return (EMSGSIZE);
+ }
+
/*
* if the interface will not calculate checksums on
* fragmented packets, then do it here.
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
-
- {
- int mhlen, firstlen = len;
- struct mbuf **mnext = &m->m_nextpkt;
- int nfrags = 1;
-
/*
* Loop through length of segment after first fragment,
* make new header and copy data of each part and link onto chain.
MGETHDR(m, M_DONTWAIT, MT_HEADER); /* MAC-OK */
if (m == 0) {
error = ENOBUFS;
- OSAddAtomic(1, (SInt32*)&ipstat.ips_odropped);
+ OSAddAtomic(1, &ipstat.ips_odropped);
goto sendorfree;
}
m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG;
if (m->m_next == 0) {
(void) m_free(m);
error = ENOBUFS; /* ??? */
- OSAddAtomic(1, (SInt32*)&ipstat.ips_odropped);
+ OSAddAtomic(1, &ipstat.ips_odropped);
goto sendorfree;
}
m->m_pkthdr.len = mhlen + len;
#if CONFIG_MACF_NET
mac_netinet_fragment(m0, m);
#endif
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(mhip->ip_off);
+#endif
+
mhip->ip_sum = 0;
if (sw_csum & CSUM_DELAY_IP) {
mhip->ip_sum = in_cksum(m, mhlen);
mnext = &m->m_nextpkt;
nfrags++;
}
- OSAddAtomic(nfrags, (SInt32*)&ipstat.ips_ofragments);
+ OSAddAtomic(nfrags, &ipstat.ips_ofragments);
/* set first/last markers for fragment chain */
m->m_flags |= M_LASTFRAG;
m->m_pkthdr.len = hlen + firstlen;
ip->ip_len = htons((u_short)m->m_pkthdr.len);
ip->ip_off |= IP_MF;
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_off);
+#endif
+
ip->ip_sum = 0;
if (sw_csum & CSUM_DELAY_IP) {
ip->ip_sum = in_cksum(m, hlen);
}
sendorfree:
+ if (error)
+ m_freem_list(m0);
- KERNEL_DEBUG(DBG_LAYER_END, ip->ip_dst.s_addr,
- ip->ip_src.s_addr, ip->ip_p, ip->ip_off, ip->ip_len);
-
- for (m = m0; m; m = m0) {
- m0 = m->m_nextpkt;
- m->m_nextpkt = 0;
-#if IPSEC
- /* clean ipsec history once it goes out of the node */
- if (ipsec_bypass == 0 && (flags & IP_NOIPSEC) == 0)
- ipsec_delaux(m);
-#endif
- if (error == 0) {
- struct rtentry *rte;
-#ifndef __APPLE__
- /* Record statistics for this interface address. */
- if (ia != NULL) {
- ia->ia_ifa.if_opackets++;
- ia->ia_ifa.if_obytes += m->m_pkthdr.len;
- }
-#endif
- if ((packetchain != 0) && (pktcnt > 0))
- panic("ip_output: mix of packet in packetlist is wrong=%p", packetlist);
- lck_mtx_lock(rt_mtx);
- if ((rte = ro->ro_rt) != NULL)
- rtref(rte);
- lck_mtx_unlock(rt_mtx);
- error = ifnet_output(ifp, PF_INET, m, rte,
- (struct sockaddr *)dst);
- if (rte != NULL)
- rtfree(rte);
- } else
- m_freem(m);
- }
-
- if (error == 0)
- OSAddAtomic(1, (SInt32*)&ipstat.ips_fragmented);
- }
-done:
- if (ia) {
- ifafree(&ia->ia_ifa);
- ia = NULL;
- }
-#if IPSEC
- if (ipsec_bypass == 0 && (flags & IP_NOIPSEC) == 0) {
- if (ro == &iproute && ro->ro_rt) {
- rtfree(ro->ro_rt);
- ro->ro_rt = NULL;
- }
- if (sp != NULL) {
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP ip_output call free SP:%x\n", sp));
- key_freesp(sp, KEY_SADB_UNLOCKED);
- }
- }
-#endif /* IPSEC */
-
- KERNEL_DEBUG(DBG_FNC_IP_OUTPUT | DBG_FUNC_END, error,0,0,0,0);
return (error);
-bad:
- m_freem(m0);
- goto done;
}
static void
/* Sometimes the IP header is not contiguous, yes this can happen! */
if (ip_offset + sizeof(struct ip) > m->m_len) {
#if DEBUG
- printf("delayed m_pullup, m->len: %ld off: %d\n",
+ printf("delayed m_pullup, m->len: %d off: %d\n",
m->m_len, ip_offset);
#endif
m_copydata(m, ip_offset, sizeof(struct ip), (caddr_t) buf);
char tmp[2];
#if DEBUG
- printf("delayed m_copyback, m->len: %ld off: %d p: %d\n",
+ printf("delayed m_copyback, m->len: %d off: %d p: %d\n",
m->m_len, offset + ip_offset, ip->ip_p);
#endif
*(u_short *)tmp = csum;
if (ip_offset + sizeof(struct ip) > m->m_len) {
#if DEBUG
- printf("in_cksum_offset - delayed m_pullup, m->len: %ld off: %lu\n",
+ printf("in_cksum_offset - delayed m_pullup, m->len: %d off: %lu\n",
m->m_len, ip_offset);
#endif
m_copydata(m, ip_offset, sizeof(struct ip), (caddr_t) buf);
char tmp[2];
#if DEBUG
- printf("in_cksum_offset m_copyback, m->len: %lu off: %lu p: %d\n",
+ printf("in_cksum_offset m_copyback, m->len: %u off: %lu p: %d\n",
m->m_len, ip_offset + offsetof(struct ip, ip_sum), ip->ip_p);
#endif
*(u_short *)tmp = ip->ip_sum;
error = EMSGSIZE;
break;
}
- MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER);
+ MGET(m, sopt->sopt_p != kernproc ? M_WAIT : M_DONTWAIT,
+ MT_HEADER);
if (m == 0) {
error = ENOBUFS;
break;
break;
if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
break;
- priv = (sopt->sopt_p != NULL &&
- proc_suser(sopt->sopt_p) != 0) ? 0 : 1;
+ priv = (proc_suser(sopt->sopt_p) == 0);
if (m) {
req = mtod(m, caddr_t);
len = m->m_len;
if (error)
break;
- if (background)
- so->so_traffic_mgt_flags |= TRAFFIC_MGT_SO_BACKGROUND;
- else
- so->so_traffic_mgt_flags &= ~TRAFFIC_MGT_SO_BACKGROUND;
+ if (background) {
+ socket_set_traffic_mgt_flags(so,
+ TRAFFIC_MGT_SO_BACKGROUND |
+ TRAFFIC_MGT_SO_BG_REGULATE);
+ } else {
+ socket_clear_traffic_mgt_flags(so,
+ TRAFFIC_MGT_SO_BACKGROUND |
+ TRAFFIC_MGT_SO_BG_REGULATE);
+ }
break;
}
struct ip_moptions **imop;
{
int error = 0;
- int i;
struct in_addr addr;
struct ip_mreq mreq;
struct ifnet *ifp = NULL;
switch (sopt->sopt_name) {
/* store an index number for the vif you wanna use in the send */
#if MROUTING
- case IP_MULTICAST_VIF:
- if (legal_vif_num == 0) {
- error = EOPNOTSUPP;
- break;
- }
- error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
- if (error)
- break;
- if (!legal_vif_num(i) && (i != -1)) {
- error = EINVAL;
+ case IP_MULTICAST_VIF:
+ {
+ int i;
+ if (legal_vif_num == 0) {
+ error = EOPNOTSUPP;
+ break;
+ }
+ error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
+ if (error)
+ break;
+ if (!legal_vif_num(i) && (i != -1)) {
+ error = EINVAL;
+ break;
+ }
+ imo->imo_multicast_vif = i;
break;
}
- imo->imo_multicast_vif = i;
- break;
#endif /* MROUTING */
case IP_MULTICAST_IF:
* If all options have default values, no need to keep the mbuf.
*/
if (imo->imo_multicast_ifp == NULL &&
- imo->imo_multicast_vif == (u_long)-1 &&
+ imo->imo_multicast_vif == (u_int32_t)-1 &&
imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL &&
imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP &&
imo->imo_num_memberships == 0) {
struct ifnet *ifp = NULL;
int error = 0;
int i;
-
+
+ bzero((caddr_t)&ro, sizeof(ro));
+
if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
error = EINVAL;
- return error;
+ goto done;
}
/*
* If no interface address was provided, use the interface of
* the route to the given multicast address.
*/
if (mreq->imr_interface.s_addr == INADDR_ANY) {
- bzero((caddr_t)&ro, sizeof(ro));
dst = (struct sockaddr_in *)&ro.ro_dst;
dst->sin_len = sizeof(*dst);
dst->sin_family = AF_INET;
dst->sin_addr = mreq->imr_multiaddr;
- lck_mtx_lock(rt_mtx);
- rtalloc_ign_locked(&ro, 0UL);
+ rtalloc_ign(&ro, 0);
if (ro.ro_rt != NULL) {
ifp = ro.ro_rt->rt_ifp;
- rtfree_locked(ro.ro_rt);
- }
- else {
+ } else {
/* If there's no default route, try using loopback */
- mreq->imr_interface.s_addr = INADDR_LOOPBACK;
+ mreq->imr_interface.s_addr = htonl(INADDR_LOOPBACK);
}
- lck_mtx_unlock(rt_mtx);
}
-
+
if (ifp == NULL) {
ifp = ip_multicast_if(&mreq->imr_interface, NULL);
}
*/
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
- return error;
+ goto done;
}
/*
* See if the membership already exists or if all the
}
if (i < imo->imo_num_memberships) {
error = EADDRINUSE;
- return error;
+ goto done;
}
if (i == IP_MAX_MEMBERSHIPS) {
error = ETOOMANYREFS;
- return error;
+ goto done;
}
/*
* Everything looks good; add a new record to the multicast
if ((imo->imo_membership[i] =
in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) {
error = ENOBUFS;
- return error;
+ goto done;
}
++imo->imo_num_memberships;
-
+
+done:
+ if (ro.ro_rt != NULL)
+ rtfree(ro.ro_rt);
+
return error;
}
IFP_TO_IA(imo->imo_multicast_ifp, ia);
addr.s_addr = (ia == NULL) ? INADDR_ANY
: IA_SIN(ia)->sin_addr.s_addr;
+ if (ia != NULL)
+ ifafree(&ia->ia_ifa);
}
error = sooptcopyout(sopt, &addr, sizeof addr);
break;
* than the interface's MTU. Can this possibly matter?
*/
ip = mtod(copym, struct ip *);
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
HTONS(ip->ip_off);
+#endif
+
ip->ip_sum = 0;
ip->ip_sum = in_cksum(copym, hlen);
/*
CSUM_IP_CHECKED | CSUM_IP_VALID;
copym->m_pkthdr.csum_data = 0xffff;
} else {
+
+#if BYTE_ORDER != BIG_ENDIAN
NTOHS(ip->ip_len);
+#endif
+
in_delayed_cksum(copym);
+
+#if BYTE_ORDER != BIG_ENDIAN
HTONS(ip->ip_len);
+#endif
+
}
}
/*
* Given a source IP address (and route, if available), determine the best
- * interface to send the packet from.
+ * interface to send the packet from. Checking for (and updating) the
+ * ROF_SRCIF_SELECTED flag in the pcb-supplied route placeholder is done
+ * without any locks based on the assumption that ip_output() is single-
+ * threaded per-pcb, i.e. for any given pcb there can only be one thread
+ * performing output at the IP layer.
*/
static struct ifaddr *
in_selectsrcif(struct ip *ip, struct route *ro, unsigned int ifscope)
{
struct ifaddr *ifa = NULL;
- struct sockaddr src = { sizeof (struct sockaddr_in), AF_INET, { 0, } };
+ struct in_addr src = ip->ip_src;
+ struct in_addr dst = ip->ip_dst;
struct ifnet *rt_ifp;
- char ip_src[16], ip_dst[16];
+ char s_src[MAX_IPv4_STR_LEN], s_dst[MAX_IPv4_STR_LEN];
if (ip_select_srcif_debug) {
- (void) inet_ntop(AF_INET, &ip->ip_src.s_addr, ip_src,
- sizeof (ip_src));
- (void) inet_ntop(AF_INET, &ip->ip_dst.s_addr, ip_dst,
- sizeof (ip_dst));
+ (void) inet_ntop(AF_INET, &src.s_addr, s_src, sizeof (s_src));
+ (void) inet_ntop(AF_INET, &dst.s_addr, s_dst, sizeof (s_dst));
}
- lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED);
+ if (ro->ro_rt != NULL)
+ RT_LOCK(ro->ro_rt);
- ((struct sockaddr_in *)&src)->sin_addr.s_addr = ip->ip_src.s_addr;
rt_ifp = (ro->ro_rt != NULL) ? ro->ro_rt->rt_ifp : NULL;
/*
scope = get_primary_ifscope();
}
- ifa = ifa_ifwithaddr_scoped(&src, scope);
+ ifa = (struct ifaddr *)ifa_foraddr_scoped(src.s_addr, scope);
+
+ if (ifa == NULL && ip->ip_p != IPPROTO_UDP &&
+ ip->ip_p != IPPROTO_TCP && ipforwarding) {
+ /*
+ * If forwarding is enabled, and if the packet isn't
+ * TCP or UDP, check if the source address belongs
+ * to one of our own interfaces; if so, demote the
+ * interface scope and do a route lookup right below.
+ */
+ ifa = (struct ifaddr *)ifa_foraddr(src.s_addr);
+ if (ifa != NULL) {
+ ifafree(ifa);
+ ifa = NULL;
+ ifscope = IFSCOPE_NONE;
+ }
+ }
if (ip_select_srcif_debug && ifa != NULL) {
if (ro->ro_rt != NULL) {
printf("%s->%s ifscope %d->%d ifa_if %s%d "
- "ro_if %s%d\n", ip_src, ip_dst, ifscope,
+ "ro_if %s%d\n", s_src, s_dst, ifscope,
scope, ifa->ifa_ifp->if_name,
ifa->ifa_ifp->if_unit, rt_ifp->if_name,
rt_ifp->if_unit);
} else {
printf("%s->%s ifscope %d->%d ifa_if %s%d\n",
- ip_src, ip_dst, ifscope, scope,
+ s_src, s_dst, ifscope, scope,
ifa->ifa_ifp->if_name,
ifa->ifa_ifp->if_unit);
}
* found interface.
*/
if (ifa == NULL && ifscope == IFSCOPE_NONE) {
- ifa = ifa_ifwithaddr(&src);
+ ifa = (struct ifaddr *)ifa_foraddr(src.s_addr);
+
+ /*
+ * If we have the IP address, but not the route, we don't
+ * really know whether or not it belongs to the correct
+ * interface (it could be shared across multiple interfaces.)
+ * The only way to find out is to do a route lookup.
+ */
+ if (ifa != NULL && ro->ro_rt == NULL) {
+ struct rtentry *rt;
+ struct sockaddr_in sin;
+ struct ifaddr *oifa = NULL;
+
+ bzero(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof (sin);
+ sin.sin_addr = dst;
+
+ lck_mtx_lock(rnh_lock);
+ if ((rt = rt_lookup(TRUE, (struct sockaddr *)&sin, NULL,
+ rt_tables[AF_INET], IFSCOPE_NONE)) != NULL) {
+ RT_LOCK(rt);
+ /*
+ * If the route uses a different interface,
+ * use that one instead. The IP address of
+ * the ifaddr that we pick up here is not
+ * relevant.
+ */
+ if (ifa->ifa_ifp != rt->rt_ifp) {
+ oifa = ifa;
+ ifa = rt->rt_ifa;
+ ifaref(ifa);
+ RT_UNLOCK(rt);
+ } else {
+ RT_UNLOCK(rt);
+ }
+ rtfree_locked(rt);
+ }
+ lck_mtx_unlock(rnh_lock);
+
+ if (oifa != NULL) {
+ struct ifaddr *iifa;
+
+ /*
+ * See if the interface pointed to by the
+ * route is configured with the source IP
+ * address of the packet.
+ */
+ iifa = (struct ifaddr *)ifa_foraddr_scoped(
+ src.s_addr, ifa->ifa_ifp->if_index);
+
+ if (iifa != NULL) {
+ /*
+ * Found it; drop the original one
+ * as well as the route interface
+ * address, and use this instead.
+ */
+ ifafree(oifa);
+ ifafree(ifa);
+ ifa = iifa;
+ } else if (!ipforwarding ||
+ (rt->rt_flags & RTF_GATEWAY)) {
+ /*
+ * This interface doesn't have that
+ * source IP address; drop the route
+ * interface address and just use the
+ * original one, and let the caller
+ * do a scoped route lookup.
+ */
+ ifafree(ifa);
+ ifa = oifa;
+ } else {
+ /*
+ * Forwarding is enabled and the source
+ * address belongs to one of our own
+ * interfaces which isn't the outgoing
+ * interface, and we have a route, and
+ * the destination is on a network that
+ * is directly attached (onlink); drop
+ * the original one and use the route
+ * interface address instead.
+ */
+ ifafree(oifa);
+ }
+ }
+ } else if (ifa != NULL && ro->ro_rt != NULL &&
+ !(ro->ro_rt->rt_flags & RTF_GATEWAY) &&
+ ifa->ifa_ifp != ro->ro_rt->rt_ifp && ipforwarding) {
+ /*
+ * Forwarding is enabled and the source address belongs
+ * to one of our own interfaces which isn't the same
+ * as the interface used by the known route; drop the
+ * original one and use the route interface address.
+ */
+ ifafree(ifa);
+ ifa = ro->ro_rt->rt_ifa;
+ ifaref(ifa);
+ }
if (ip_select_srcif_debug && ifa != NULL) {
printf("%s->%s ifscope %d ifa_if %s%d\n",
- ip_src, ip_dst, ifscope, ifa->ifa_ifp->if_name,
+ s_src, s_dst, ifscope, ifa->ifa_ifp->if_name,
ifa->ifa_ifp->if_unit);
}
}
+ if (ro->ro_rt != NULL)
+ RT_LOCK_ASSERT_HELD(ro->ro_rt);
/*
* If there is a non-loopback route with the wrong interface, or if
* there is no interface configured with such an address, blow it
if (ifa != NULL) {
printf("%s->%s ifscope %d ro_if %s%d != "
"ifa_if %s%d (cached route cleared)\n",
- ip_src, ip_dst, ifscope, rt_ifp->if_name,
+ s_src, s_dst, ifscope, rt_ifp->if_name,
rt_ifp->if_unit, ifa->ifa_ifp->if_name,
ifa->ifa_ifp->if_unit);
} else {
printf("%s->%s ifscope %d ro_if %s%d "
"(no ifa_if found)\n",
- ip_src, ip_dst, ifscope, rt_ifp->if_name,
+ s_src, s_dst, ifscope, rt_ifp->if_name,
rt_ifp->if_unit);
}
}
- rtfree_locked(ro->ro_rt);
+ RT_UNLOCK(ro->ro_rt);
+ rtfree(ro->ro_rt);
ro->ro_rt = NULL;
ro->ro_flags &= ~ROF_SRCIF_SELECTED;
* but other shared subnets; for now we explicitly test only
* for the former case and save the latter for future.
*/
- if (IN_LINKLOCAL(ntohl(ip->ip_dst.s_addr)) &&
- !IN_LINKLOCAL(ntohl(ip->ip_src.s_addr)) && ifa != NULL) {
+ if (IN_LINKLOCAL(ntohl(dst.s_addr)) &&
+ !IN_LINKLOCAL(ntohl(src.s_addr)) && ifa != NULL) {
ifafree(ifa);
ifa = NULL;
}
if (ip_select_srcif_debug && ifa == NULL) {
printf("%s->%s ifscope %d (neither ro_if/ifa_if found)\n",
- ip_src, ip_dst, ifscope);
+ s_src, s_dst, ifscope);
}
/*
* otherwise we want to come back here again when the route points
* to the interface over which the ARP reply arrives on.
*/
- if (ro->ro_rt != NULL && (!IN_LINKLOCAL(ntohl(ip->ip_dst.s_addr)) ||
+ if (ro->ro_rt != NULL && (!IN_LINKLOCAL(ntohl(dst.s_addr)) ||
(ro->ro_rt->rt_gateway->sa_family == AF_LINK &&
SDL(ro->ro_rt->rt_gateway)->sdl_alen != 0))) {
ro->ro_flags |= ROF_SRCIF_SELECTED;
ro->ro_rt->generation_id = route_generation;
}
+ if (ro->ro_rt != NULL)
+ RT_UNLOCK(ro->ro_rt);
+
return (ifa);
}
else
inp->inp_flags |= INP_BOUND_IF;
- lck_mtx_lock(rt_mtx);
/* Blow away any cached route in the PCB */
if (inp->inp_route.ro_rt != NULL) {
- rtfree_locked(inp->inp_route.ro_rt);
+ rtfree(inp->inp_route.ro_rt);
inp->inp_route.ro_rt = NULL;
}
- lck_mtx_unlock(rt_mtx);
}