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