📄 ip_output.c
字号:
ip->ip_sum = 0;
/*
* XXX
* delayed checksums are not currently compatible with IPsec
*/
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
HTONS(ip->ip_len);
HTONS(ip->ip_off);
error = ipsec4_output(&state, sp, flags);
m = state.m;
if (flags & IP_ROUTETOIF) {
/*
* if we have tunnel mode SA, we may need to ignore
* IP_ROUTETOIF.
*/
if (state.ro != &iproute || state.ro->ro_rt != NULL) {
flags &= ~IP_ROUTETOIF;
ro = state.ro;
}
} else
ro = state.ro;
dst = (struct sockaddr_in *)state.dst;
if (error) {
/* mbuf is already reclaimed in ipsec4_output. */
m0 = NULL;
switch (error) {
case EHOSTUNREACH:
case ENETUNREACH:
case EMSGSIZE:
case ENOBUFS:
case ENOMEM:
break;
default:
printf("ip4_output (ipsec): error code %d\n", error);
/*fall through*/
case ENOENT:
/* don't show these error codes to the user */
error = 0;
break;
}
goto bad;
}
/* be sure to update variables that are affected by ipsec4_output() */
ip = mtod(m, struct ip *);
#ifdef _IP_VHL
hlen = IP_VHL_HL(ip->ip_vhl) << 2;
#else
hlen = ip->ip_hl << 2;
#endif
if (ro->ro_rt == NULL) {
if ((flags & IP_ROUTETOIF) == 0) {
printf("ip_output: "
"can't update route after IPsec processing\n");
error = EHOSTUNREACH; /*XXX*/
goto bad;
}
} else {
if (state.encap) {
ia = ifatoia(ro->ro_rt->rt_ifa);
ifp = ro->ro_rt->rt_ifp;
}
}
}
/* make it flipped, again. */
NTOHS(ip->ip_len);
NTOHS(ip->ip_off);
skip_ipsec:
#endif /*IPSEC*/
/*
* IpHack's section.
* - Xlate: translate packet's addr/port (NAT).
* - Firewall: deny/allow/etc.
* - Wrap: fake packet's addr/port <unimpl.>
* - Encapsulate: put it in another IP and send out. <unimp.>
*/
if (fr_checkp) {
struct mbuf *m1 = m;
if ((error = (*fr_checkp)(ip, hlen, ifp, 1, &m1)) || !m1)
goto done;
ip = mtod(m = m1, struct ip *);
}
/*
* Check with the firewall...
*/
if (fw_enable && ip_fw_chk_ptr) {
struct sockaddr_in *old = dst;
off = (*ip_fw_chk_ptr)(&ip,
hlen, ifp, &divert_cookie, &m, &rule, &dst);
/*
* On return we must do the following:
* IP_FW_PORT_DENY_FLAG -> drop the pkt (XXX new)
* 1<=off<= 0xffff -> DIVERT
* (off & IP_FW_PORT_DYNT_FLAG) -> send to a DUMMYNET pipe
* (off & IP_FW_PORT_TEE_FLAG) -> TEE the packet
* dst != old -> IPFIREWALL_FORWARD
* off==0, dst==old -> accept
* If some of the above modules is not compiled in, then
* we should't have to check the corresponding condition
* (because the ipfw control socket should not accept
* unsupported rules), but better play safe and drop
* packets in case of doubt.
*/
if ( (off & IP_FW_PORT_DENY_FLAG) || m == NULL) {
if (m)
m_freem(m);
error = EACCES ;
goto done ;
}
ip = mtod(m, struct ip *);
if (off == 0 && dst == old) /* common case */
goto pass ;
#ifdef DUMMYNET
if ((off & IP_FW_PORT_DYNT_FLAG) != 0) {
/*
* pass the pkt to dummynet. Need to include
* pipe number, m, ifp, ro, dst because these are
* not recomputed in the next pass.
* All other parameters have been already used and
* so they are not needed anymore.
* XXX note: if the ifp or ro entry are deleted
* while a pkt is in dummynet, we are in trouble!
*/
error = dummynet_io(off & 0xffff, DN_TO_IP_OUT, m,
ifp,ro,dst,rule, flags);
goto done;
}
#endif
#ifdef IPDIVERT
if (off != 0 && (off & IP_FW_PORT_DYNT_FLAG) == 0) {
struct mbuf *clone = NULL;
/* Clone packet if we're doing a 'tee' */
if ((off & IP_FW_PORT_TEE_FLAG) != 0)
clone = m_dup(m, M_DONTWAIT);
/*
* XXX
* delayed checksums are not currently compatible
* with divert sockets.
*/
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
/* Restore packet header fields to original values */
HTONS(ip->ip_len);
HTONS(ip->ip_off);
/* Deliver packet to divert input routine */
ip_divert_cookie = divert_cookie;
divert_packet(m, 0, off & 0xffff);
/* If 'tee', continue with original packet */
if (clone != NULL) {
m = clone;
ip = mtod(m, struct ip *);
goto pass;
}
goto done;
}
#endif
#ifdef IPFIREWALL_FORWARD
/* Here we check dst to make sure it's directly reachable on the
* interface we previously thought it was.
* If it isn't (which may be likely in some situations) we have
* to re-route it (ie, find a route for the next-hop and the
* associated interface) and set them here. This is nested
* forwarding which in most cases is undesirable, except where
* such control is nigh impossible. So we do it here.
* And I'm babbling.
*/
if (off == 0 && old != dst) {
struct in_ifaddr *ia;
/* It's changed... */
/* There must be a better way to do this next line... */
static struct route sro_fwd, *ro_fwd = &sro_fwd;
#ifdef IPFIREWALL_FORWARD_DEBUG
printf("IPFIREWALL_FORWARD: New dst ip: ");
print_ip(dst->sin_addr);
printf("\n");
#endif
/*
* We need to figure out if we have been forwarded
* to a local socket. If so then we should somehow
* "loop back" to ip_input, and get directed to the
* PCB as if we had received this packet. This is
* because it may be dificult to identify the packets
* you want to forward until they are being output
* and have selected an interface. (e.g. locally
* initiated packets) If we used the loopback inteface,
* we would not be able to control what happens
* as the packet runs through ip_input() as
* it is done through a ISR.
*/
TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
/*
* If the addr to forward to is one
* of ours, we pretend to
* be the destination for this packet.
*/
if (IA_SIN(ia)->sin_addr.s_addr ==
dst->sin_addr.s_addr)
break;
}
if (ia) {
/* tell ip_input "dont filter" */
ip_fw_fwd_addr = dst;
if (m->m_pkthdr.rcvif == NULL)
m->m_pkthdr.rcvif = ifunit("lo0");
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
m->m_pkthdr.csum_flags |=
CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
m0->m_pkthdr.csum_data = 0xffff;
}
m->m_pkthdr.csum_flags |=
CSUM_IP_CHECKED | CSUM_IP_VALID;
HTONS(ip->ip_len);
HTONS(ip->ip_off);
ip_input(m);
goto done;
}
/* Some of the logic for this was
* nicked from above.
*
* This rewrites the cached route in a local PCB.
* Is this what we want to do?
*/
bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst));
ro_fwd->ro_rt = 0;
rtalloc_ign(ro_fwd, RTF_PRCLONING);
if (ro_fwd->ro_rt == 0) {
ipstat.ips_noroute++;
error = EHOSTUNREACH;
goto bad;
}
ia = ifatoia(ro_fwd->ro_rt->rt_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)
isbroadcast =
(ro_fwd->ro_rt->rt_flags & RTF_BROADCAST);
else
isbroadcast = in_broadcast(dst->sin_addr, ifp);
RTFREE(ro->ro_rt);
ro->ro_rt = ro_fwd->ro_rt;
dst = (struct sockaddr_in *)&ro_fwd->ro_dst;
/*
* 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)->sin_addr;
goto pass ;
}
#endif /* IPFIREWALL_FORWARD */
/*
* if we get here, none of the above matches, and
* we have to drop the pkt
*/
m_freem(m);
error = EACCES; /* not sure this is the right error msg */
goto done;
}
pass:
m->m_pkthdr.csum_flags |= CSUM_IP;
sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist;
if (sw_csum & CSUM_DELAY_DATA) {
in_delayed_cksum(m);
sw_csum &= ~CSUM_DELAY_DATA;
}
m->m_pkthdr.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 ||
ifp->if_hwassist & CSUM_FRAGMENT) {
HTONS(ip->ip_len);
HTONS(ip->ip_off);
ip->ip_sum = 0;
if (sw_csum & CSUM_DELAY_IP) {
if (ip->ip_vhl == IP_VHL_BORING) {
ip->ip_sum = in_cksum_hdr(ip);
} else {
ip->ip_sum = in_cksum(m, hlen);
}
}
/* Record statistics for this interface address. */
if (!(flags & IP_FORWARDING) && ia != NULL) {
ia->ia_ifa.if_opackets++;
ia->ia_ifa.if_obytes += m->m_pkthdr.len;
}
#ifdef IPSEC
/* clean ipsec history once it goes out of the node */
ipsec_delaux(m);
#endif
error = (*ifp->if_output)(ifp, m,
(struct sockaddr *)dst, ro->ro_rt);
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) {
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.
*/
if ((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;
}
ipstat.ips_cantfrag++;
goto bad;
}
len = (ifp->if_mtu - hlen) &~ 7;
if (len < 8) {
error = EMSGSIZE;
goto bad;
}
/*
* if the interface will not calculate checksums on
* fragmented packets, then do it here.
*/
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA &&
(ifp->if_hwassist & CSUM_IP_FRAGS) == 0) {
in_delayed_cksum(m);
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.
*/
m0 = m;
mhlen = sizeof (struct ip);
for (off = hlen + len; off < (u_short)ip->ip_len; off += len) {
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == 0) {
error = ENOBUFS;
ipstat.ips_odropped++;
goto sendorfree;
}
m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG;
m->m_data += max_linkhdr;
mhip = mtod(m, struct ip *);
*mhip = *ip;
if (hlen > sizeof (struct ip)) {
mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
mhip->ip_vhl = IP_MAKE_VHL(IPVERSION, mhlen >> 2);
}
m->m_len = mhlen;
mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
if (ip->ip_off & IP_MF)
mhip->ip_off |= IP_MF;
if (off + len >= (u_short)ip->ip_len)
len = (u_short)ip->ip_len - off;
else
mhip->ip_off |= IP_MF;
mhip->ip_len = htons((u_short)(len + mhlen));
m->m_next = m_copy(m0, off, len);
if (m->m_next == 0) {
(void) m_free(m);
error = ENOBUFS; /* ??? */
ipstat.ips_odropped++;
goto sendorfree;
}
m->m_pkthdr.len = mhlen + len;
m->m_pkthdr.rcvif = (struct ifnet *)0;
m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags;
HTONS(mhip->ip_off);
mhip->ip_sum = 0;
if (sw_csum & CSUM_DELAY_IP) {
if (mhip->ip_vhl == IP_VHL_BORING) {
mhip->ip_sum = in_cksum_hdr(mhip);
} else {
mhip->ip_sum = in_cksum(m, mhlen);
}
}
*mnext = m;
mnext = &m->m_nextpkt;
nfrags++;
}
ipstat.ips_ofragments += nfrags;
/* set first/last markers for fragment chain */
m->m_flags |= M_LASTFRAG;
m0->m_flags |= M_FIRSTFRAG | M_FRAG;
m0->m_pkthdr.csum_data = nfrags;
/*
* Update first fragment by trimming what's been copied out
* and updating header, then send each fragment (in order).
*/
m = m0;
m_adj(m, hlen + firstlen - (u_short)ip->ip_len);
m->m_pkthdr.len = hlen + firstlen;
ip->ip_len = htons((u_short)m->m_pkthdr.len);
ip->ip_off |= IP_MF;
HTONS(ip->ip_off);
ip->ip_sum = 0;
if (sw_csum & CSUM_DELAY_IP) {
if (ip->ip_vhl == IP_VHL_BORING) {
ip->ip_sum = in_cksum_hdr(ip);
} else {
ip->ip_sum = in_cksum(m, hlen);
}
}
sendorfree:
for (m = m0; m; m = m0) {
m0 = m->m_nextpkt;
m->m_nextpkt = 0;
#ifdef IPSEC
/* clean ipsec history once it goes out of the node */
ipsec_delaux(m);
#endif
if (error == 0) {
/* Record statistics for this interface address. */
if (ia != NULL) {
ia->ia_ifa.if_opackets++;
ia->ia_ifa.if_obytes += m->m_pkthdr.len;
}
error = (*ifp->if_output)(ifp, m,
(struct sockaddr *)dst, ro->ro_rt);
} else
m_freem(m);
}
if (error == 0)
ipstat.ips_fragmented++;
}
done:
#ifdef IPSEC
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:%p\n", sp));
key_freesp(sp);
}
#endif /* IPSEC */
return (error);
bad:
m_freem(m0);
goto done;
}
void
in_delayed_cksum(struct mbuf *m)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -