ip_output.c
来自「eCos操作系统源码」· C语言 代码 · 共 1,972 行 · 第 1/4 页
C
1,972 行
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;}voidin_delayed_cksum(struct mbuf *m){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?