1 /* ip_output.c 1.23 82/01/19 */ 2 3 #include "../h/param.h" 4 #include "../h/mbuf.h" 5 #include "../h/mtpr.h" 6 #include "../h/socket.h" 7 #include "../h/socketvar.h" 8 #include "../net/in.h" 9 #include "../net/in_systm.h" 10 #include "../net/if.h" 11 #include "../net/ip.h" 12 #include "../net/ip_var.h" 13 14 ip_output(m, opt) 15 struct mbuf *m; 16 struct mbuf *opt; 17 { 18 register struct ip *ip = mtod(m, struct ip *); 19 register struct ifnet *ifp; 20 int len, hlen = sizeof (struct ip), off; 21 22 COUNT(IP_OUTPUT); 23 if (opt) /* XXX */ 24 (void) m_free(opt); /* XXX */ 25 /* 26 * Fill in IP header. 27 */ 28 ip->ip_v = IPVERSION; 29 ip->ip_hl = hlen >> 2; 30 ip->ip_off &= IP_DF; 31 ip->ip_id = htons(ip_id++); 32 33 /* 34 * Find interface for this packet. 35 */ 36 ifp = if_ifonnetof(ip->ip_dst); 37 if (ifp == 0) { 38 ifp = if_gatewayfor(ip->ip_dst); 39 if (ifp == 0) 40 goto bad; 41 } 42 43 /* 44 * If small enough for interface, can just send directly. 45 */ 46 if (ip->ip_len <= ifp->if_mtu) { 47 #if vax 48 ip->ip_len = htons((u_short)ip->ip_len); 49 ip->ip_off = htons((u_short)ip->ip_off); 50 #endif 51 ip->ip_sum = 0; 52 ip->ip_sum = in_cksum(m, hlen); 53 return ((*ifp->if_output)(ifp, m, PF_INET)); 54 } 55 56 /* 57 * Too large for interface; fragment if possible. 58 * Must be able to put at least 8 bytes per fragment. 59 */ 60 if (ip->ip_off & IP_DF) 61 goto bad; 62 len = (ifp->if_mtu - hlen) &~ 7; 63 if (len < 8) 64 goto bad; 65 66 /* 67 * Discard IP header from logical mbuf for m_copy's sake. 68 * Loop through length of segment, make a copy of each 69 * part and output. 70 */ 71 m->m_len -= sizeof (struct ip); 72 m->m_off += sizeof (struct ip); 73 for (off = 0; off < ip->ip_len; off += len) { 74 struct mbuf *mh = m_get(M_DONTWAIT); 75 struct ip *mhip; 76 77 if (mh == 0) 78 goto bad; 79 mh->m_off = MMAXOFF - hlen; 80 mhip = mtod(mh, struct ip *); 81 *mhip = *ip; 82 if (hlen > sizeof (struct ip)) { 83 int olen = ip_optcopy(ip, mhip, off); 84 mh->m_len = sizeof (struct ip) + olen; 85 } else 86 mh->m_len = sizeof (struct ip); 87 mhip->ip_off = off; 88 if (off + len >= ip->ip_len) 89 mhip->ip_len = ip->ip_len - off; 90 else { 91 mhip->ip_len = len; 92 mhip->ip_off |= IP_MF; 93 } 94 mhip->ip_len = htons((u_short)(mhip->ip_len + sizeof (struct ip))); 95 mh->m_next = m_copy(m, off, len); 96 if (mh->m_next == 0) { 97 (void) m_free(mh); 98 goto bad; 99 } 100 ip->ip_off = htons((u_short)ip->ip_off); 101 ip->ip_sum = 0; 102 ip->ip_sum = in_cksum(m, hlen); 103 if ((*ifp->if_output)(ifp, mh, PF_INET) == 0) 104 goto bad; 105 } 106 m_freem(m); 107 return (1); 108 bad: 109 m_freem(m); 110 return (0); 111 } 112 113 /* 114 * Copy options from ip to jp. 115 * If off is 0 all options are copied 116 * otherwise copy selectively. 117 */ 118 ip_optcopy(ip, jp, off) 119 struct ip *ip, *jp; 120 int off; 121 { 122 register u_char *cp, *dp; 123 int opt, optlen, cnt; 124 125 COUNT(IP_OPTCOPY); 126 cp = (u_char *)(ip + 1); 127 dp = (u_char *)(jp + 1); 128 cnt = (ip->ip_hl << 2) - sizeof (struct ip); 129 for (; cnt > 0; cnt -= optlen, cp += optlen) { 130 opt = cp[0]; 131 if (opt == IPOPT_EOL) 132 break; 133 if (opt == IPOPT_NOP) 134 optlen = 1; 135 else 136 optlen = cp[1]; 137 if (optlen > cnt) /* XXX */ 138 optlen = cnt; /* XXX */ 139 if (off == 0 || IPOPT_COPIED(opt)) { 140 bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen); 141 dp += optlen; 142 } 143 } 144 for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) 145 *dp++ = IPOPT_EOL; 146 return (optlen); 147 } 148