1*6505Ssam /* ip_output.c 1.32 82/04/10 */ 24571Swnj 34496Swnj #include "../h/param.h" 44662Swnj #include "../h/mbuf.h" 54724Swnj #include "../h/mtpr.h" 64662Swnj #include "../h/socket.h" 74924Swnj #include "../h/socketvar.h" 85085Swnj #include "../net/in.h" 95085Swnj #include "../net/in_systm.h" 105085Swnj #include "../net/if.h" 114802Swnj #include "../net/ip.h" 124899Swnj #include "../net/ip_var.h" 136339Ssam #include "../net/route.h" 14*6505Ssam #include <errno.h> 154496Swnj 166339Ssam ip_output(m, opt, ro, allowbroadcast) 174924Swnj struct mbuf *m; 185085Swnj struct mbuf *opt; 196339Ssam struct route *ro; 206211Swnj int allowbroadcast; 214496Swnj { 224924Swnj register struct ip *ip = mtod(m, struct ip *); 235085Swnj register struct ifnet *ifp; 24*6505Ssam int len, hlen = sizeof (struct ip), off, error = 0; 256339Ssam struct route iproute; 266351Ssam struct sockaddr *dst; 274496Swnj 284496Swnj COUNT(IP_OUTPUT); 295219Swnj if (opt) /* XXX */ 305242Sroot (void) m_free(opt); /* XXX */ 314924Swnj /* 324924Swnj * Fill in IP header. 334924Swnj */ 344924Swnj ip->ip_v = IPVERSION; 354924Swnj ip->ip_hl = hlen >> 2; 364924Swnj ip->ip_off &= IP_DF; 375085Swnj ip->ip_id = htons(ip_id++); 384496Swnj 394545Swnj /* 406351Ssam * Find interface for this packet in the routing 416351Ssam * table. Note each interface has placed itself 426377Ssam * in there at boot time, so calls to rtalloc 436377Ssam * degenerate to if_ifonnetof(ip->ip_dst.s_net). 445085Swnj */ 456339Ssam if (ro == 0) { 466339Ssam ro = &iproute; 476339Ssam bzero((caddr_t)ro, sizeof (*ro)); 485085Swnj } 496339Ssam if (ro->ro_rt == 0) { 506368Ssam ro->ro_dst.sa_family = AF_INET; 516368Ssam ((struct sockaddr_in *)&ro->ro_dst)->sin_addr = ip->ip_dst; 526368Ssam rtalloc(ro); 536339Ssam } 546368Ssam if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) { 55*6505Ssam extern int ipprintfs; 56*6505Ssam 57*6505Ssam if (ipprintfs) 58*6505Ssam printf("no route to %x (from %x, len %d)\n", 59*6505Ssam ip->ip_dst.s_addr, ip->ip_src.s_addr, ip->ip_len); 60*6505Ssam error = ENETUNREACH; 616211Swnj goto bad; 626412Ssam } 63*6505Ssam dst = ro->ro_rt->rt_flags & RTF_DIRECT ? 646368Ssam (struct sockaddr *)&ro->ro_dst : &ro->ro_rt->rt_gateway; 656412Ssam if (ro == &iproute) 666412Ssam RTFREE(ro->ro_rt); 676339Ssam if (!allowbroadcast && (ifp->if_flags & IFF_BROADCAST)) { 686339Ssam struct sockaddr_in *sin; 696339Ssam 706339Ssam sin = (struct sockaddr_in *)&ifp->if_broadaddr; 71*6505Ssam if (sin->sin_addr.s_addr == ip->ip_dst.s_addr) { 72*6505Ssam error = EPERM; /* ??? */ 736339Ssam goto bad; 74*6505Ssam } 756339Ssam } 766339Ssam 775085Swnj /* 784924Swnj * If small enough for interface, can just send directly. 794545Swnj */ 805085Swnj if (ip->ip_len <= ifp->if_mtu) { 815219Swnj #if vax 825085Swnj ip->ip_len = htons((u_short)ip->ip_len); 835085Swnj ip->ip_off = htons((u_short)ip->ip_off); 845219Swnj #endif 855085Swnj ip->ip_sum = 0; 865085Swnj ip->ip_sum = in_cksum(m, hlen); 876377Ssam ro->ro_rt->rt_use++; 886351Ssam return ((*ifp->if_output)(ifp, m, dst)); 894908Swnj } 904924Swnj 914924Swnj /* 924924Swnj * Too large for interface; fragment if possible. 934924Swnj * Must be able to put at least 8 bytes per fragment. 944924Swnj */ 95*6505Ssam if (ip->ip_off & IP_DF) { 96*6505Ssam error = EMSGSIZE; 974924Swnj goto bad; 98*6505Ssam } 995085Swnj len = (ifp->if_mtu - hlen) &~ 7; 100*6505Ssam if (len < 8) { 101*6505Ssam error = EMSGSIZE; 1024924Swnj goto bad; 103*6505Ssam } 1044924Swnj 1054924Swnj /* 1064924Swnj * Discard IP header from logical mbuf for m_copy's sake. 1074924Swnj * Loop through length of segment, make a copy of each 1084924Swnj * part and output. 1094924Swnj */ 1104924Swnj m->m_len -= sizeof (struct ip); 1114924Swnj m->m_off += sizeof (struct ip); 1125892Sroot for (off = 0; off < ip->ip_len-hlen; off += len) { 1135584Sroot struct mbuf *mh = m_get(M_DONTWAIT); 1144924Swnj struct ip *mhip; 1154924Swnj 116*6505Ssam if (mh == 0) { 117*6505Ssam error = ENOBUFS; 1184924Swnj goto bad; 119*6505Ssam } 1204924Swnj mh->m_off = MMAXOFF - hlen; 1214924Swnj mhip = mtod(mh, struct ip *); 1224924Swnj *mhip = *ip; 1234952Swnj if (hlen > sizeof (struct ip)) { 1244924Swnj int olen = ip_optcopy(ip, mhip, off); 1254924Swnj mh->m_len = sizeof (struct ip) + olen; 1264924Swnj } else 1274924Swnj mh->m_len = sizeof (struct ip); 1285770Swnj mhip->ip_off = off >> 3; 1295892Sroot if (off + len >= ip->ip_len-hlen) 1305892Sroot len = mhip->ip_len = ip->ip_len - hlen - off; 1314924Swnj else { 1324924Swnj mhip->ip_len = len; 1334924Swnj mhip->ip_off |= IP_MF; 1344496Swnj } 1355770Swnj mhip->ip_len += sizeof (struct ip); 1365770Swnj #if vax 1375770Swnj mhip->ip_len = htons((u_short)mhip->ip_len); 1385770Swnj #endif 1394924Swnj mh->m_next = m_copy(m, off, len); 1404924Swnj if (mh->m_next == 0) { 1414967Swnj (void) m_free(mh); 142*6505Ssam error = ENOBUFS; /* ??? */ 1434924Swnj goto bad; 1444674Swnj } 1455770Swnj #if vax 1465892Sroot mhip->ip_off = htons((u_short)mhip->ip_off); 1475770Swnj #endif 1485892Sroot mhip->ip_sum = 0; 1495892Sroot mhip->ip_sum = in_cksum(mh, hlen); 1506377Ssam ro->ro_rt->rt_use++; 151*6505Ssam if (error = (*ifp->if_output)(ifp, mh, dst)) 152*6505Ssam break; 1534924Swnj } 1544924Swnj bad: 1554924Swnj m_freem(m); 156*6505Ssam return (error); 1574924Swnj } 1584924Swnj 1594924Swnj /* 1604924Swnj * Copy options from ip to jp. 1614952Swnj * If off is 0 all options are copied 1624924Swnj * otherwise copy selectively. 1634924Swnj */ 1644924Swnj ip_optcopy(ip, jp, off) 1654924Swnj struct ip *ip, *jp; 1664924Swnj int off; 1674924Swnj { 1684924Swnj register u_char *cp, *dp; 1694924Swnj int opt, optlen, cnt; 1704924Swnj 1714952Swnj COUNT(IP_OPTCOPY); 1724924Swnj cp = (u_char *)(ip + 1); 1734924Swnj dp = (u_char *)(jp + 1); 1744924Swnj cnt = (ip->ip_hl << 2) - sizeof (struct ip); 1754924Swnj for (; cnt > 0; cnt -= optlen, cp += optlen) { 1764924Swnj opt = cp[0]; 1774924Swnj if (opt == IPOPT_EOL) 1784924Swnj break; 1794924Swnj if (opt == IPOPT_NOP) 1804924Swnj optlen = 1; 1814924Swnj else 1824924Swnj optlen = cp[1]; 1834924Swnj if (optlen > cnt) /* XXX */ 1844924Swnj optlen = cnt; /* XXX */ 1854924Swnj if (off == 0 || IPOPT_COPIED(opt)) { 1864952Swnj bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen); 1874924Swnj dp += optlen; 1884674Swnj } 1894545Swnj } 1904924Swnj for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) 1914924Swnj *dp++ = IPOPT_EOL; 1924924Swnj return (optlen); 1934496Swnj } 194