123745Skarels /* 229144Smckusick * Copyright (c) 1982, 1986 Regents of the University of California. 323745Skarels * All rights reserved. The Berkeley software License Agreement 423745Skarels * specifies the terms and conditions for redistribution. 523745Skarels * 6*31200Skarels * @(#)ip_output.c 7.5 (Berkeley) 05/24/87 723745Skarels */ 84571Swnj 917061Sbloom #include "param.h" 1017061Sbloom #include "mbuf.h" 1117061Sbloom #include "errno.h" 1224814Skarels #include "protosw.h" 1317061Sbloom #include "socket.h" 1417061Sbloom #include "socketvar.h" 1510893Ssam 1610893Ssam #include "../net/if.h" 1710893Ssam #include "../net/route.h" 1810893Ssam 1917061Sbloom #include "in.h" 2024814Skarels #include "in_pcb.h" 2117061Sbloom #include "in_systm.h" 2218375Skarels #include "in_var.h" 2317061Sbloom #include "ip.h" 2417061Sbloom #include "ip_var.h" 254496Swnj 2612460Ssam #ifdef vax 2729923Skarels #include "../machine/mtpr.h" 2812460Ssam #endif 2910893Ssam 3024814Skarels struct mbuf *ip_insertoptions(); 3124814Skarels 3224814Skarels /* 3324814Skarels * IP output. The packet in mbuf chain m contains a skeletal IP 3431037Skarels * header (with len, off, ttl, proto, tos, src, dst). 3531037Skarels * The mbuf chain containing the packet will be freed. 3631037Skarels * The mbuf opt, if present, will not be freed. 3724814Skarels */ 3812417Ssam ip_output(m, opt, ro, flags) 394924Swnj struct mbuf *m; 405085Swnj struct mbuf *opt; 416339Ssam struct route *ro; 4212417Ssam int flags; 434496Swnj { 4424814Skarels register struct ip *ip; 455085Swnj register struct ifnet *ifp; 466505Ssam int len, hlen = sizeof (struct ip), off, error = 0; 476339Ssam struct route iproute; 4816602Ssam struct sockaddr_in *dst; 494496Swnj 5024814Skarels if (opt) 5124814Skarels m = ip_insertoptions(m, opt, &hlen); 5224814Skarels ip = mtod(m, struct ip *); 534924Swnj /* 544924Swnj * Fill in IP header. 554924Swnj */ 5612417Ssam if ((flags & IP_FORWARDING) == 0) { 5712417Ssam ip->ip_v = IPVERSION; 5812417Ssam ip->ip_off &= IP_DF; 5912417Ssam ip->ip_id = htons(ip_id++); 6016545Skarels ip->ip_hl = hlen >> 2; 6124814Skarels } else 6224814Skarels hlen = ip->ip_hl << 2; 634496Swnj 644545Swnj /* 657155Swnj * Route packet. 665085Swnj */ 676339Ssam if (ro == 0) { 686339Ssam ro = &iproute; 696339Ssam bzero((caddr_t)ro, sizeof (*ro)); 705085Swnj } 7116602Ssam dst = (struct sockaddr_in *)&ro->ro_dst; 7226156Skarels /* 7326156Skarels * If there is a cached route, 7426156Skarels * check that it is to the same destination 7526156Skarels * and is still up. If not, free it and try again. 7626156Skarels */ 7726156Skarels if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 7826156Skarels dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { 7926156Skarels RTFREE(ro->ro_rt); 8026156Skarels ro->ro_rt = (struct rtentry *)0; 8126156Skarels } 826339Ssam if (ro->ro_rt == 0) { 8316602Ssam dst->sin_family = AF_INET; 8416602Ssam dst->sin_addr = ip->ip_dst; 8526058Skarels } 8626058Skarels /* 8726058Skarels * If routing to interface only, 8826058Skarels * short circuit routing lookup. 8926058Skarels */ 9026058Skarels if (flags & IP_ROUTETOIF) { 9126058Skarels struct in_ifaddr *ia; 9227196Skarels 9327973Skarels ia = (struct in_ifaddr *)ifa_ifwithdstaddr(dst); 9427196Skarels if (ia == 0) 9527196Skarels ia = in_iaonnetof(in_netof(ip->ip_dst)); 9626058Skarels if (ia == 0) { 9726058Skarels error = ENETUNREACH; 9826058Skarels goto bad; 9926058Skarels } 10026058Skarels ifp = ia->ia_ifp; 10126058Skarels } else { 10226058Skarels if (ro->ro_rt == 0) 10326058Skarels rtalloc(ro); 10426058Skarels if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) { 105*31200Skarels if (in_localaddr(ip->ip_dst)) 106*31200Skarels error = EHOSTUNREACH; 107*31200Skarels else 108*31200Skarels error = ENETUNREACH; 10926058Skarels goto bad; 11026058Skarels } 11126058Skarels ro->ro_rt->rt_use++; 11230761Skarels if (ro->ro_rt->rt_flags & RTF_GATEWAY) 11326058Skarels dst = (struct sockaddr_in *)&ro->ro_rt->rt_gateway; 1146339Ssam } 11523745Skarels #ifndef notdef 1167155Swnj /* 11723745Skarels * If source address not specified yet, use address 11823745Skarels * of outgoing interface. 11923745Skarels */ 12023745Skarels if (ip->ip_src.s_addr == INADDR_ANY) { 12123745Skarels register struct in_ifaddr *ia; 12223745Skarels 12323745Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 12423745Skarels if (ia->ia_ifp == ifp) { 12523745Skarels ip->ip_src = IA_SIN(ia)->sin_addr; 12623745Skarels break; 12723745Skarels } 12823745Skarels } 12923745Skarels #endif 13023745Skarels /* 13110402Ssam * Look for broadcast address and 13210402Ssam * and verify user is allowed to send 13310146Ssam * such a packet. 1347155Swnj */ 13518375Skarels if (in_broadcast(dst->sin_addr)) { 13610146Ssam if ((ifp->if_flags & IFF_BROADCAST) == 0) { 13710146Ssam error = EADDRNOTAVAIL; 13810146Ssam goto bad; 13910146Ssam } 14012417Ssam if ((flags & IP_ALLOWBROADCAST) == 0) { 1417155Swnj error = EACCES; 1426339Ssam goto bad; 1436505Ssam } 14410146Ssam /* don't allow broadcast messages to be fragmented */ 14510146Ssam if (ip->ip_len > ifp->if_mtu) { 14610146Ssam error = EMSGSIZE; 14710146Ssam goto bad; 14810146Ssam } 1496339Ssam } 1506339Ssam 1515085Swnj /* 1524924Swnj * If small enough for interface, can just send directly. 1534545Swnj */ 1545085Swnj if (ip->ip_len <= ifp->if_mtu) { 1555085Swnj ip->ip_len = htons((u_short)ip->ip_len); 1565085Swnj ip->ip_off = htons((u_short)ip->ip_off); 1575085Swnj ip->ip_sum = 0; 1585085Swnj ip->ip_sum = in_cksum(m, hlen); 15916602Ssam error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); 1607155Swnj goto done; 1614908Swnj } 1624924Swnj 1634924Swnj /* 1644924Swnj * Too large for interface; fragment if possible. 1654924Swnj * Must be able to put at least 8 bytes per fragment. 1664924Swnj */ 1676505Ssam if (ip->ip_off & IP_DF) { 1686505Ssam error = EMSGSIZE; 1694924Swnj goto bad; 1706505Ssam } 1715085Swnj len = (ifp->if_mtu - hlen) &~ 7; 1726505Ssam if (len < 8) { 1736505Ssam error = EMSGSIZE; 1744924Swnj goto bad; 1756505Ssam } 1764924Swnj 1774924Swnj /* 1784924Swnj * Discard IP header from logical mbuf for m_copy's sake. 1794924Swnj * Loop through length of segment, make a copy of each 1804924Swnj * part and output. 1814924Swnj */ 1824924Swnj m->m_len -= sizeof (struct ip); 1834924Swnj m->m_off += sizeof (struct ip); 1845892Sroot for (off = 0; off < ip->ip_len-hlen; off += len) { 18510012Ssam struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER); 1864924Swnj struct ip *mhip; 1874924Swnj 1886505Ssam if (mh == 0) { 1896505Ssam error = ENOBUFS; 1904924Swnj goto bad; 1916505Ssam } 1924924Swnj mh->m_off = MMAXOFF - hlen; 1934924Swnj mhip = mtod(mh, struct ip *); 1944924Swnj *mhip = *ip; 1954952Swnj if (hlen > sizeof (struct ip)) { 1964924Swnj int olen = ip_optcopy(ip, mhip, off); 1974924Swnj mh->m_len = sizeof (struct ip) + olen; 1984924Swnj } else 1994924Swnj mh->m_len = sizeof (struct ip); 20027738Skarels mhip->ip_off = (off >> 3) + (ip->ip_off & ~IP_MF); 20116545Skarels if (ip->ip_off & IP_MF) 20216545Skarels mhip->ip_off |= IP_MF; 2035892Sroot if (off + len >= ip->ip_len-hlen) 2045892Sroot len = mhip->ip_len = ip->ip_len - hlen - off; 2054924Swnj else { 2064924Swnj mhip->ip_len = len; 2074924Swnj mhip->ip_off |= IP_MF; 2084496Swnj } 2095770Swnj mhip->ip_len += sizeof (struct ip); 2105770Swnj mhip->ip_len = htons((u_short)mhip->ip_len); 2114924Swnj mh->m_next = m_copy(m, off, len); 2124924Swnj if (mh->m_next == 0) { 2134967Swnj (void) m_free(mh); 2146505Ssam error = ENOBUFS; /* ??? */ 2154924Swnj goto bad; 2164674Swnj } 2175892Sroot mhip->ip_off = htons((u_short)mhip->ip_off); 2185892Sroot mhip->ip_sum = 0; 2195892Sroot mhip->ip_sum = in_cksum(mh, hlen); 22016602Ssam if (error = (*ifp->if_output)(ifp, mh, (struct sockaddr *)dst)) 2216505Ssam break; 2224924Swnj } 2234924Swnj bad: 2244924Swnj m_freem(m); 2257155Swnj done: 22612417Ssam if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) 2277155Swnj RTFREE(ro->ro_rt); 2286505Ssam return (error); 2294924Swnj } 2304924Swnj 2314924Swnj /* 23224814Skarels * Insert IP options into preformed packet. 23324814Skarels * Adjust IP destination as required for IP source routing, 23424814Skarels * as indicated by a non-zero in_addr at the start of the options. 23524814Skarels */ 23624814Skarels struct mbuf * 23724814Skarels ip_insertoptions(m, opt, phlen) 23824814Skarels register struct mbuf *m; 23924814Skarels struct mbuf *opt; 24024814Skarels int *phlen; 24124814Skarels { 24224814Skarels register struct ipoption *p = mtod(opt, struct ipoption *); 24324814Skarels struct mbuf *n; 24424814Skarels register struct ip *ip = mtod(m, struct ip *); 24526385Skarels unsigned optlen; 24624814Skarels 24724814Skarels optlen = opt->m_len - sizeof(p->ipopt_dst); 24824814Skarels if (p->ipopt_dst.s_addr) 24924814Skarels ip->ip_dst = p->ipopt_dst; 25024814Skarels if (m->m_off >= MMAXOFF || MMINOFF + optlen > m->m_off) { 25124814Skarels MGET(n, M_DONTWAIT, MT_HEADER); 25224814Skarels if (n == 0) 25324814Skarels return (m); 25424814Skarels m->m_len -= sizeof(struct ip); 25524814Skarels m->m_off += sizeof(struct ip); 25624814Skarels n->m_next = m; 25724814Skarels m = n; 25824814Skarels m->m_off = MMAXOFF - sizeof(struct ip) - optlen; 25924814Skarels m->m_len = optlen + sizeof(struct ip); 26024814Skarels bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 26124814Skarels } else { 26224814Skarels m->m_off -= optlen; 26324814Skarels m->m_len += optlen; 26424814Skarels ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 26524814Skarels } 26624814Skarels ip = mtod(m, struct ip *); 26726385Skarels bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen); 26824814Skarels *phlen = sizeof(struct ip) + optlen; 26924814Skarels ip->ip_len += optlen; 27024814Skarels return (m); 27124814Skarels } 27224814Skarels 27324814Skarels /* 2744924Swnj * Copy options from ip to jp. 2754952Swnj * If off is 0 all options are copied 2764924Swnj * otherwise copy selectively. 2774924Swnj */ 2784924Swnj ip_optcopy(ip, jp, off) 2794924Swnj struct ip *ip, *jp; 2804924Swnj int off; 2814924Swnj { 2824924Swnj register u_char *cp, *dp; 2834924Swnj int opt, optlen, cnt; 2844924Swnj 2854924Swnj cp = (u_char *)(ip + 1); 2864924Swnj dp = (u_char *)(jp + 1); 2874924Swnj cnt = (ip->ip_hl << 2) - sizeof (struct ip); 2884924Swnj for (; cnt > 0; cnt -= optlen, cp += optlen) { 2894924Swnj opt = cp[0]; 2904924Swnj if (opt == IPOPT_EOL) 2914924Swnj break; 2924924Swnj if (opt == IPOPT_NOP) 2934924Swnj optlen = 1; 2944924Swnj else 29524814Skarels optlen = cp[IPOPT_OLEN]; 2964924Swnj if (optlen > cnt) /* XXX */ 2974924Swnj optlen = cnt; /* XXX */ 2984924Swnj if (off == 0 || IPOPT_COPIED(opt)) { 2994952Swnj bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen); 3004924Swnj dp += optlen; 3014674Swnj } 3024545Swnj } 3034924Swnj for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) 3044924Swnj *dp++ = IPOPT_EOL; 3054924Swnj return (optlen); 3064496Swnj } 30724814Skarels 30824814Skarels /* 30924814Skarels * IP socket option processing. 31024814Skarels */ 31124814Skarels ip_ctloutput(op, so, level, optname, m) 31224814Skarels int op; 31324814Skarels struct socket *so; 31424814Skarels int level, optname; 31524814Skarels struct mbuf **m; 31624814Skarels { 31724814Skarels int error = 0; 31824814Skarels struct inpcb *inp = sotoinpcb(so); 31924814Skarels 32024814Skarels if (level != IPPROTO_IP) 32124814Skarels error = EINVAL; 32224814Skarels else switch (op) { 32324814Skarels 32424814Skarels case PRCO_SETOPT: 32524814Skarels switch (optname) { 32624814Skarels case IP_OPTIONS: 32726036Skarels return (ip_pcbopts(&inp->inp_options, *m)); 32824814Skarels 32924814Skarels default: 33024814Skarels error = EINVAL; 33124814Skarels break; 33224814Skarels } 33324814Skarels break; 33424814Skarels 33524814Skarels case PRCO_GETOPT: 33624814Skarels switch (optname) { 33724814Skarels case IP_OPTIONS: 33824814Skarels *m = m_get(M_WAIT, MT_SOOPTS); 33924814Skarels if (inp->inp_options) { 34024814Skarels (*m)->m_off = inp->inp_options->m_off; 34124814Skarels (*m)->m_len = inp->inp_options->m_len; 34224814Skarels bcopy(mtod(inp->inp_options, caddr_t), 34326385Skarels mtod(*m, caddr_t), (unsigned)(*m)->m_len); 34424814Skarels } else 34524814Skarels (*m)->m_len = 0; 34624814Skarels break; 34724814Skarels default: 34824814Skarels error = EINVAL; 34924814Skarels break; 35024814Skarels } 35124814Skarels break; 35224814Skarels } 35324814Skarels if (op == PRCO_SETOPT) 35426385Skarels (void)m_free(*m); 35524814Skarels return (error); 35624814Skarels } 35724814Skarels 35824814Skarels /* 35926036Skarels * Set up IP options in pcb for insertion in output packets. 36026036Skarels * Store in mbuf with pointer in pcbopt, adding pseudo-option 36126036Skarels * with destination address if source routed. 36224814Skarels */ 36326036Skarels ip_pcbopts(pcbopt, m) 36426036Skarels struct mbuf **pcbopt; 36526036Skarels register struct mbuf *m; 36624814Skarels { 36724814Skarels register cnt, optlen; 36824814Skarels register u_char *cp; 36924814Skarels u_char opt; 37024814Skarels 37124814Skarels /* turn off any old options */ 37226036Skarels if (*pcbopt) 37326385Skarels (void)m_free(*pcbopt); 37426036Skarels *pcbopt = 0; 37524814Skarels if (m == (struct mbuf *)0 || m->m_len == 0) { 37624814Skarels /* 37724814Skarels * Only turning off any previous options. 37824814Skarels */ 37924814Skarels if (m) 38026385Skarels (void)m_free(m); 38124814Skarels return (0); 38224814Skarels } 38324814Skarels 38424814Skarels #ifndef vax 38524814Skarels if (m->m_len % sizeof(long)) 38624814Skarels goto bad; 38724814Skarels #endif 38824814Skarels /* 38924814Skarels * IP first-hop destination address will be stored before 39024814Skarels * actual options; move other options back 39124814Skarels * and clear it when none present. 39224814Skarels */ 39324814Skarels #if MAX_IPOPTLEN >= MMAXOFF - MMINOFF 39424814Skarels if (m->m_off + m->m_len + sizeof(struct in_addr) > MAX_IPOPTLEN) 39524814Skarels goto bad; 39624814Skarels #else 39724814Skarels if (m->m_off + m->m_len + sizeof(struct in_addr) > MMAXOFF) 39824814Skarels goto bad; 39924814Skarels #endif 40024814Skarels cnt = m->m_len; 40124814Skarels m->m_len += sizeof(struct in_addr); 40224814Skarels cp = mtod(m, u_char *) + sizeof(struct in_addr); 40326385Skarels ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt); 40424814Skarels bzero(mtod(m, caddr_t), sizeof(struct in_addr)); 40524814Skarels 40624814Skarels for (; cnt > 0; cnt -= optlen, cp += optlen) { 40724814Skarels opt = cp[IPOPT_OPTVAL]; 40824814Skarels if (opt == IPOPT_EOL) 40924814Skarels break; 41024814Skarels if (opt == IPOPT_NOP) 41124814Skarels optlen = 1; 41224814Skarels else { 41324814Skarels optlen = cp[IPOPT_OLEN]; 41424814Skarels if (optlen <= IPOPT_OLEN || optlen > cnt) 41524814Skarels goto bad; 41624814Skarels } 41724814Skarels switch (opt) { 41824814Skarels 41924814Skarels default: 42024814Skarels break; 42124814Skarels 42224814Skarels case IPOPT_LSRR: 42324814Skarels case IPOPT_SSRR: 42424814Skarels /* 42524814Skarels * user process specifies route as: 42624814Skarels * ->A->B->C->D 42724814Skarels * D must be our final destination (but we can't 42824814Skarels * check that since we may not have connected yet). 42924814Skarels * A is first hop destination, which doesn't appear in 43024814Skarels * actual IP option, but is stored before the options. 43124814Skarels */ 43224814Skarels if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) 43324814Skarels goto bad; 43424814Skarels m->m_len -= sizeof(struct in_addr); 43524814Skarels cnt -= sizeof(struct in_addr); 43624814Skarels optlen -= sizeof(struct in_addr); 43724814Skarels cp[IPOPT_OLEN] = optlen; 43824814Skarels /* 43924814Skarels * Move first hop before start of options. 44024814Skarels */ 44126385Skarels bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), 44224814Skarels sizeof(struct in_addr)); 44324814Skarels /* 44424814Skarels * Then copy rest of options back 44524814Skarels * to close up the deleted entry. 44624814Skarels */ 44726385Skarels ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] + 44826385Skarels sizeof(struct in_addr)), 44926385Skarels (caddr_t)&cp[IPOPT_OFFSET+1], 45026385Skarels (unsigned)cnt + sizeof(struct in_addr)); 45124814Skarels break; 45224814Skarels } 45324814Skarels } 45426036Skarels *pcbopt = m; 45524814Skarels return (0); 45624814Skarels 45724814Skarels bad: 45826385Skarels (void)m_free(m); 45924814Skarels return (EINVAL); 46024814Skarels } 461