123745Skarels /* 223745Skarels * Copyright (c) 1982 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*26156Skarels * @(#)ip_output.c 6.12 (Berkeley) 02/12/86 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 2712460Ssam #include "../vax/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 3424814Skarels * header (as ipovly). The mbuf chain containing the packet will 3524814Skarels * be freed. The mbuf opt, if present, will not be freed. 3624814Skarels */ 3712417Ssam ip_output(m, opt, ro, flags) 384924Swnj struct mbuf *m; 395085Swnj struct mbuf *opt; 406339Ssam struct route *ro; 4112417Ssam int flags; 424496Swnj { 4324814Skarels register struct ip *ip; 445085Swnj register struct ifnet *ifp; 456505Ssam int len, hlen = sizeof (struct ip), off, error = 0; 466339Ssam struct route iproute; 4716602Ssam struct sockaddr_in *dst; 484496Swnj 4924814Skarels if (opt) 5024814Skarels m = ip_insertoptions(m, opt, &hlen); 5124814Skarels ip = mtod(m, struct ip *); 524924Swnj /* 534924Swnj * Fill in IP header. 544924Swnj */ 5512417Ssam if ((flags & IP_FORWARDING) == 0) { 5612417Ssam ip->ip_v = IPVERSION; 5712417Ssam ip->ip_off &= IP_DF; 5812417Ssam ip->ip_id = htons(ip_id++); 5916545Skarels ip->ip_hl = hlen >> 2; 6024814Skarels } else 6124814Skarels hlen = ip->ip_hl << 2; 624496Swnj 634545Swnj /* 647155Swnj * Route packet. 655085Swnj */ 666339Ssam if (ro == 0) { 676339Ssam ro = &iproute; 686339Ssam bzero((caddr_t)ro, sizeof (*ro)); 695085Swnj } 7016602Ssam dst = (struct sockaddr_in *)&ro->ro_dst; 71*26156Skarels /* 72*26156Skarels * If there is a cached route, 73*26156Skarels * check that it is to the same destination 74*26156Skarels * and is still up. If not, free it and try again. 75*26156Skarels */ 76*26156Skarels if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 77*26156Skarels dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { 78*26156Skarels RTFREE(ro->ro_rt); 79*26156Skarels ro->ro_rt = (struct rtentry *)0; 80*26156Skarels } 816339Ssam if (ro->ro_rt == 0) { 8216602Ssam dst->sin_family = AF_INET; 8316602Ssam dst->sin_addr = ip->ip_dst; 8426058Skarels } 8526058Skarels /* 8626058Skarels * If routing to interface only, 8726058Skarels * short circuit routing lookup. 8826058Skarels */ 8926058Skarels if (flags & IP_ROUTETOIF) { 9026058Skarels struct in_ifaddr *ia; 9126058Skarels ia = in_iaonnetof(in_netof(ip->ip_dst)); 9226058Skarels if (ia == 0) { 9326058Skarels error = ENETUNREACH; 9426058Skarels goto bad; 9526058Skarels } 9626058Skarels ifp = ia->ia_ifp; 9726058Skarels } else { 9826058Skarels if (ro->ro_rt == 0) 9926058Skarels rtalloc(ro); 10026058Skarels if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) { 10126058Skarels error = ENETUNREACH; 10226058Skarels goto bad; 10326058Skarels } 10426058Skarels ro->ro_rt->rt_use++; 10526058Skarels if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST)) 10626058Skarels dst = (struct sockaddr_in *)&ro->ro_rt->rt_gateway; 1076339Ssam } 10823745Skarels #ifndef notdef 1097155Swnj /* 11023745Skarels * If source address not specified yet, use address 11123745Skarels * of outgoing interface. 11223745Skarels */ 11323745Skarels if (ip->ip_src.s_addr == INADDR_ANY) { 11423745Skarels register struct in_ifaddr *ia; 11523745Skarels 11623745Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 11723745Skarels if (ia->ia_ifp == ifp) { 11823745Skarels ip->ip_src = IA_SIN(ia)->sin_addr; 11923745Skarels break; 12023745Skarels } 12123745Skarels } 12223745Skarels #endif 12323745Skarels /* 12410402Ssam * Look for broadcast address and 12510402Ssam * and verify user is allowed to send 12610146Ssam * such a packet. 1277155Swnj */ 12818375Skarels if (in_broadcast(dst->sin_addr)) { 12910146Ssam if ((ifp->if_flags & IFF_BROADCAST) == 0) { 13010146Ssam error = EADDRNOTAVAIL; 13110146Ssam goto bad; 13210146Ssam } 13312417Ssam if ((flags & IP_ALLOWBROADCAST) == 0) { 1347155Swnj error = EACCES; 1356339Ssam goto bad; 1366505Ssam } 13710146Ssam /* don't allow broadcast messages to be fragmented */ 13810146Ssam if (ip->ip_len > ifp->if_mtu) { 13910146Ssam error = EMSGSIZE; 14010146Ssam goto bad; 14110146Ssam } 1426339Ssam } 1436339Ssam 1445085Swnj /* 1454924Swnj * If small enough for interface, can just send directly. 1464545Swnj */ 1475085Swnj if (ip->ip_len <= ifp->if_mtu) { 1485085Swnj ip->ip_len = htons((u_short)ip->ip_len); 1495085Swnj ip->ip_off = htons((u_short)ip->ip_off); 1505085Swnj ip->ip_sum = 0; 1515085Swnj ip->ip_sum = in_cksum(m, hlen); 15216602Ssam error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); 1537155Swnj goto done; 1544908Swnj } 1554924Swnj 1564924Swnj /* 1574924Swnj * Too large for interface; fragment if possible. 1584924Swnj * Must be able to put at least 8 bytes per fragment. 1594924Swnj */ 1606505Ssam if (ip->ip_off & IP_DF) { 1616505Ssam error = EMSGSIZE; 1624924Swnj goto bad; 1636505Ssam } 1645085Swnj len = (ifp->if_mtu - hlen) &~ 7; 1656505Ssam if (len < 8) { 1666505Ssam error = EMSGSIZE; 1674924Swnj goto bad; 1686505Ssam } 1694924Swnj 1704924Swnj /* 1714924Swnj * Discard IP header from logical mbuf for m_copy's sake. 1724924Swnj * Loop through length of segment, make a copy of each 1734924Swnj * part and output. 1744924Swnj */ 1754924Swnj m->m_len -= sizeof (struct ip); 1764924Swnj m->m_off += sizeof (struct ip); 1775892Sroot for (off = 0; off < ip->ip_len-hlen; off += len) { 17810012Ssam struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER); 1794924Swnj struct ip *mhip; 1804924Swnj 1816505Ssam if (mh == 0) { 1826505Ssam error = ENOBUFS; 1834924Swnj goto bad; 1846505Ssam } 1854924Swnj mh->m_off = MMAXOFF - hlen; 1864924Swnj mhip = mtod(mh, struct ip *); 1874924Swnj *mhip = *ip; 1884952Swnj if (hlen > sizeof (struct ip)) { 1894924Swnj int olen = ip_optcopy(ip, mhip, off); 1904924Swnj mh->m_len = sizeof (struct ip) + olen; 1914924Swnj } else 1924924Swnj mh->m_len = sizeof (struct ip); 1935770Swnj mhip->ip_off = off >> 3; 19416545Skarels if (ip->ip_off & IP_MF) 19516545Skarels mhip->ip_off |= IP_MF; 1965892Sroot if (off + len >= ip->ip_len-hlen) 1975892Sroot len = mhip->ip_len = ip->ip_len - hlen - off; 1984924Swnj else { 1994924Swnj mhip->ip_len = len; 2004924Swnj mhip->ip_off |= IP_MF; 2014496Swnj } 2025770Swnj mhip->ip_len += sizeof (struct ip); 2035770Swnj mhip->ip_len = htons((u_short)mhip->ip_len); 2044924Swnj mh->m_next = m_copy(m, off, len); 2054924Swnj if (mh->m_next == 0) { 2064967Swnj (void) m_free(mh); 2076505Ssam error = ENOBUFS; /* ??? */ 2084924Swnj goto bad; 2094674Swnj } 2105892Sroot mhip->ip_off = htons((u_short)mhip->ip_off); 2115892Sroot mhip->ip_sum = 0; 2125892Sroot mhip->ip_sum = in_cksum(mh, hlen); 21316602Ssam if (error = (*ifp->if_output)(ifp, mh, (struct sockaddr *)dst)) 2146505Ssam break; 2154924Swnj } 2164924Swnj bad: 2174924Swnj m_freem(m); 2187155Swnj done: 21912417Ssam if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) 2207155Swnj RTFREE(ro->ro_rt); 2216505Ssam return (error); 2224924Swnj } 2234924Swnj 2244924Swnj /* 22524814Skarels * Insert IP options into preformed packet. 22624814Skarels * Adjust IP destination as required for IP source routing, 22724814Skarels * as indicated by a non-zero in_addr at the start of the options. 22824814Skarels */ 22924814Skarels struct mbuf * 23024814Skarels ip_insertoptions(m, opt, phlen) 23124814Skarels register struct mbuf *m; 23224814Skarels struct mbuf *opt; 23324814Skarels int *phlen; 23424814Skarels { 23524814Skarels register struct ipoption *p = mtod(opt, struct ipoption *); 23624814Skarels struct mbuf *n; 23724814Skarels register struct ip *ip = mtod(m, struct ip *); 23824814Skarels int optlen; 23924814Skarels 24024814Skarels optlen = opt->m_len - sizeof(p->ipopt_dst); 24124814Skarels if (p->ipopt_dst.s_addr) 24224814Skarels ip->ip_dst = p->ipopt_dst; 24324814Skarels if (m->m_off >= MMAXOFF || MMINOFF + optlen > m->m_off) { 24424814Skarels MGET(n, M_DONTWAIT, MT_HEADER); 24524814Skarels if (n == 0) 24624814Skarels return (m); 24724814Skarels m->m_len -= sizeof(struct ip); 24824814Skarels m->m_off += sizeof(struct ip); 24924814Skarels n->m_next = m; 25024814Skarels m = n; 25124814Skarels m->m_off = MMAXOFF - sizeof(struct ip) - optlen; 25224814Skarels m->m_len = optlen + sizeof(struct ip); 25324814Skarels bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 25424814Skarels } else { 25524814Skarels m->m_off -= optlen; 25624814Skarels m->m_len += optlen; 25724814Skarels ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 25824814Skarels } 25924814Skarels ip = mtod(m, struct ip *); 26024814Skarels bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), optlen); 26124814Skarels *phlen = sizeof(struct ip) + optlen; 26224814Skarels ip->ip_len += optlen; 26324814Skarels return (m); 26424814Skarels } 26524814Skarels 26624814Skarels /* 2674924Swnj * Copy options from ip to jp. 2684952Swnj * If off is 0 all options are copied 2694924Swnj * otherwise copy selectively. 2704924Swnj */ 2714924Swnj ip_optcopy(ip, jp, off) 2724924Swnj struct ip *ip, *jp; 2734924Swnj int off; 2744924Swnj { 2754924Swnj register u_char *cp, *dp; 2764924Swnj int opt, optlen, cnt; 2774924Swnj 2784924Swnj cp = (u_char *)(ip + 1); 2794924Swnj dp = (u_char *)(jp + 1); 2804924Swnj cnt = (ip->ip_hl << 2) - sizeof (struct ip); 2814924Swnj for (; cnt > 0; cnt -= optlen, cp += optlen) { 2824924Swnj opt = cp[0]; 2834924Swnj if (opt == IPOPT_EOL) 2844924Swnj break; 2854924Swnj if (opt == IPOPT_NOP) 2864924Swnj optlen = 1; 2874924Swnj else 28824814Skarels optlen = cp[IPOPT_OLEN]; 2894924Swnj if (optlen > cnt) /* XXX */ 2904924Swnj optlen = cnt; /* XXX */ 2914924Swnj if (off == 0 || IPOPT_COPIED(opt)) { 2924952Swnj bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen); 2934924Swnj dp += optlen; 2944674Swnj } 2954545Swnj } 2964924Swnj for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) 2974924Swnj *dp++ = IPOPT_EOL; 2984924Swnj return (optlen); 2994496Swnj } 30024814Skarels 30124814Skarels /* 30224814Skarels * IP socket option processing. 30324814Skarels */ 30424814Skarels ip_ctloutput(op, so, level, optname, m) 30524814Skarels int op; 30624814Skarels struct socket *so; 30724814Skarels int level, optname; 30824814Skarels struct mbuf **m; 30924814Skarels { 31024814Skarels int error = 0; 31124814Skarels struct inpcb *inp = sotoinpcb(so); 31224814Skarels 31324814Skarels if (level != IPPROTO_IP) 31424814Skarels error = EINVAL; 31524814Skarels else switch (op) { 31624814Skarels 31724814Skarels case PRCO_SETOPT: 31824814Skarels switch (optname) { 31924814Skarels case IP_OPTIONS: 32026036Skarels return (ip_pcbopts(&inp->inp_options, *m)); 32124814Skarels 32224814Skarels default: 32324814Skarels error = EINVAL; 32424814Skarels break; 32524814Skarels } 32624814Skarels break; 32724814Skarels 32824814Skarels case PRCO_GETOPT: 32924814Skarels switch (optname) { 33024814Skarels case IP_OPTIONS: 33124814Skarels *m = m_get(M_WAIT, MT_SOOPTS); 33224814Skarels if (inp->inp_options) { 33324814Skarels (*m)->m_off = inp->inp_options->m_off; 33424814Skarels (*m)->m_len = inp->inp_options->m_len; 33524814Skarels bcopy(mtod(inp->inp_options, caddr_t), 33624814Skarels mtod(*m, caddr_t), (*m)->m_len); 33724814Skarels } else 33824814Skarels (*m)->m_len = 0; 33924814Skarels break; 34024814Skarels default: 34124814Skarels error = EINVAL; 34224814Skarels break; 34324814Skarels } 34424814Skarels break; 34524814Skarels } 34624814Skarels if (op == PRCO_SETOPT) 34724814Skarels m_free(*m); 34824814Skarels return (error); 34924814Skarels } 35024814Skarels 35124814Skarels /* 35226036Skarels * Set up IP options in pcb for insertion in output packets. 35326036Skarels * Store in mbuf with pointer in pcbopt, adding pseudo-option 35426036Skarels * with destination address if source routed. 35524814Skarels */ 35626036Skarels ip_pcbopts(pcbopt, m) 35726036Skarels struct mbuf **pcbopt; 35826036Skarels register struct mbuf *m; 35924814Skarels { 36024814Skarels register cnt, optlen; 36124814Skarels register u_char *cp; 36224814Skarels u_char opt; 36324814Skarels 36424814Skarels /* turn off any old options */ 36526036Skarels if (*pcbopt) 36626036Skarels m_free(*pcbopt); 36726036Skarels *pcbopt = 0; 36824814Skarels if (m == (struct mbuf *)0 || m->m_len == 0) { 36924814Skarels /* 37024814Skarels * Only turning off any previous options. 37124814Skarels */ 37224814Skarels if (m) 37324814Skarels m_free(m); 37424814Skarels return (0); 37524814Skarels } 37624814Skarels 37724814Skarels #ifndef vax 37824814Skarels if (m->m_len % sizeof(long)) 37924814Skarels goto bad; 38024814Skarels #endif 38124814Skarels /* 38224814Skarels * IP first-hop destination address will be stored before 38324814Skarels * actual options; move other options back 38424814Skarels * and clear it when none present. 38524814Skarels */ 38624814Skarels #if MAX_IPOPTLEN >= MMAXOFF - MMINOFF 38724814Skarels if (m->m_off + m->m_len + sizeof(struct in_addr) > MAX_IPOPTLEN) 38824814Skarels goto bad; 38924814Skarels #else 39024814Skarels if (m->m_off + m->m_len + sizeof(struct in_addr) > MMAXOFF) 39124814Skarels goto bad; 39224814Skarels #endif 39324814Skarels cnt = m->m_len; 39424814Skarels m->m_len += sizeof(struct in_addr); 39524814Skarels cp = mtod(m, u_char *) + sizeof(struct in_addr); 39624814Skarels ovbcopy(mtod(m, caddr_t), cp, cnt); 39724814Skarels bzero(mtod(m, caddr_t), sizeof(struct in_addr)); 39824814Skarels 39924814Skarels for (; cnt > 0; cnt -= optlen, cp += optlen) { 40024814Skarels opt = cp[IPOPT_OPTVAL]; 40124814Skarels if (opt == IPOPT_EOL) 40224814Skarels break; 40324814Skarels if (opt == IPOPT_NOP) 40424814Skarels optlen = 1; 40524814Skarels else { 40624814Skarels optlen = cp[IPOPT_OLEN]; 40724814Skarels if (optlen <= IPOPT_OLEN || optlen > cnt) 40824814Skarels goto bad; 40924814Skarels } 41024814Skarels switch (opt) { 41124814Skarels 41224814Skarels default: 41324814Skarels break; 41424814Skarels 41524814Skarels case IPOPT_LSRR: 41624814Skarels case IPOPT_SSRR: 41724814Skarels /* 41824814Skarels * user process specifies route as: 41924814Skarels * ->A->B->C->D 42024814Skarels * D must be our final destination (but we can't 42124814Skarels * check that since we may not have connected yet). 42224814Skarels * A is first hop destination, which doesn't appear in 42324814Skarels * actual IP option, but is stored before the options. 42424814Skarels */ 42524814Skarels if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) 42624814Skarels goto bad; 42724814Skarels m->m_len -= sizeof(struct in_addr); 42824814Skarels cnt -= sizeof(struct in_addr); 42924814Skarels optlen -= sizeof(struct in_addr); 43024814Skarels cp[IPOPT_OLEN] = optlen; 43124814Skarels /* 43224814Skarels * Move first hop before start of options. 43324814Skarels */ 43424814Skarels bcopy(&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), 43524814Skarels sizeof(struct in_addr)); 43624814Skarels /* 43724814Skarels * Then copy rest of options back 43824814Skarels * to close up the deleted entry. 43924814Skarels */ 44024814Skarels ovbcopy(&cp[IPOPT_OFFSET+1] + sizeof(struct in_addr), 44124814Skarels &cp[IPOPT_OFFSET+1], cnt + sizeof(struct in_addr)); 44224814Skarels break; 44324814Skarels } 44424814Skarels } 44526036Skarels *pcbopt = m; 44624814Skarels return (0); 44724814Skarels 44824814Skarels bad: 44924814Skarels m_free(m); 45024814Skarels return (EINVAL); 45124814Skarels } 452