123745Skarels /* 229144Smckusick * Copyright (c) 1982, 1986 Regents of the University of California. 3*32787Sbostic * All rights reserved. 423745Skarels * 5*32787Sbostic * Redistribution and use in source and binary forms are permitted 6*32787Sbostic * provided that this notice is preserved and that due credit is given 7*32787Sbostic * to the University of California at Berkeley. The name of the University 8*32787Sbostic * may not be used to endorse or promote products derived from this 9*32787Sbostic * software without specific prior written permission. This software 10*32787Sbostic * is provided ``as is'' without express or implied warranty. 11*32787Sbostic * 12*32787Sbostic * @(#)ip_output.c 7.7 (Berkeley) 12/07/87 1323745Skarels */ 144571Swnj 1517061Sbloom #include "param.h" 1617061Sbloom #include "mbuf.h" 1717061Sbloom #include "errno.h" 1824814Skarels #include "protosw.h" 1917061Sbloom #include "socket.h" 2017061Sbloom #include "socketvar.h" 2110893Ssam 2210893Ssam #include "../net/if.h" 2310893Ssam #include "../net/route.h" 2410893Ssam 2517061Sbloom #include "in.h" 2624814Skarels #include "in_pcb.h" 2717061Sbloom #include "in_systm.h" 2818375Skarels #include "in_var.h" 2917061Sbloom #include "ip.h" 3017061Sbloom #include "ip_var.h" 314496Swnj 3212460Ssam #ifdef vax 3329923Skarels #include "../machine/mtpr.h" 3412460Ssam #endif 3510893Ssam 3624814Skarels struct mbuf *ip_insertoptions(); 3724814Skarels 3824814Skarels /* 3924814Skarels * IP output. The packet in mbuf chain m contains a skeletal IP 4031037Skarels * header (with len, off, ttl, proto, tos, src, dst). 4131037Skarels * The mbuf chain containing the packet will be freed. 4231037Skarels * The mbuf opt, if present, will not be freed. 4324814Skarels */ 4412417Ssam ip_output(m, opt, ro, flags) 454924Swnj struct mbuf *m; 465085Swnj struct mbuf *opt; 476339Ssam struct route *ro; 4812417Ssam int flags; 494496Swnj { 5024814Skarels register struct ip *ip; 515085Swnj register struct ifnet *ifp; 526505Ssam int len, hlen = sizeof (struct ip), off, error = 0; 536339Ssam struct route iproute; 5416602Ssam struct sockaddr_in *dst; 554496Swnj 5624814Skarels if (opt) 5724814Skarels m = ip_insertoptions(m, opt, &hlen); 5824814Skarels ip = mtod(m, struct ip *); 594924Swnj /* 604924Swnj * Fill in IP header. 614924Swnj */ 6212417Ssam if ((flags & IP_FORWARDING) == 0) { 6312417Ssam ip->ip_v = IPVERSION; 6412417Ssam ip->ip_off &= IP_DF; 6512417Ssam ip->ip_id = htons(ip_id++); 6616545Skarels ip->ip_hl = hlen >> 2; 6724814Skarels } else 6824814Skarels hlen = ip->ip_hl << 2; 694496Swnj 704545Swnj /* 717155Swnj * Route packet. 725085Swnj */ 736339Ssam if (ro == 0) { 746339Ssam ro = &iproute; 756339Ssam bzero((caddr_t)ro, sizeof (*ro)); 765085Swnj } 7716602Ssam dst = (struct sockaddr_in *)&ro->ro_dst; 7826156Skarels /* 7926156Skarels * If there is a cached route, 8026156Skarels * check that it is to the same destination 8126156Skarels * and is still up. If not, free it and try again. 8226156Skarels */ 8326156Skarels if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 8426156Skarels dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { 8526156Skarels RTFREE(ro->ro_rt); 8626156Skarels ro->ro_rt = (struct rtentry *)0; 8726156Skarels } 886339Ssam if (ro->ro_rt == 0) { 8916602Ssam dst->sin_family = AF_INET; 9016602Ssam dst->sin_addr = ip->ip_dst; 9126058Skarels } 9226058Skarels /* 9326058Skarels * If routing to interface only, 9426058Skarels * short circuit routing lookup. 9526058Skarels */ 9626058Skarels if (flags & IP_ROUTETOIF) { 9726058Skarels struct in_ifaddr *ia; 9827196Skarels 9927973Skarels ia = (struct in_ifaddr *)ifa_ifwithdstaddr(dst); 10027196Skarels if (ia == 0) 10127196Skarels ia = in_iaonnetof(in_netof(ip->ip_dst)); 10226058Skarels if (ia == 0) { 10326058Skarels error = ENETUNREACH; 10426058Skarels goto bad; 10526058Skarels } 10626058Skarels ifp = ia->ia_ifp; 10726058Skarels } else { 10826058Skarels if (ro->ro_rt == 0) 10926058Skarels rtalloc(ro); 11026058Skarels if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) { 11131200Skarels if (in_localaddr(ip->ip_dst)) 11231200Skarels error = EHOSTUNREACH; 11331200Skarels else 11431200Skarels error = ENETUNREACH; 11526058Skarels goto bad; 11626058Skarels } 11726058Skarels ro->ro_rt->rt_use++; 11830761Skarels if (ro->ro_rt->rt_flags & RTF_GATEWAY) 11926058Skarels dst = (struct sockaddr_in *)&ro->ro_rt->rt_gateway; 1206339Ssam } 12123745Skarels #ifndef notdef 1227155Swnj /* 12323745Skarels * If source address not specified yet, use address 12423745Skarels * of outgoing interface. 12523745Skarels */ 12623745Skarels if (ip->ip_src.s_addr == INADDR_ANY) { 12723745Skarels register struct in_ifaddr *ia; 12823745Skarels 12923745Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 13023745Skarels if (ia->ia_ifp == ifp) { 13123745Skarels ip->ip_src = IA_SIN(ia)->sin_addr; 13223745Skarels break; 13323745Skarels } 13423745Skarels } 13523745Skarels #endif 13623745Skarels /* 13710402Ssam * Look for broadcast address and 13810402Ssam * and verify user is allowed to send 13910146Ssam * such a packet. 1407155Swnj */ 14118375Skarels if (in_broadcast(dst->sin_addr)) { 14210146Ssam if ((ifp->if_flags & IFF_BROADCAST) == 0) { 14310146Ssam error = EADDRNOTAVAIL; 14410146Ssam goto bad; 14510146Ssam } 14612417Ssam if ((flags & IP_ALLOWBROADCAST) == 0) { 1477155Swnj error = EACCES; 1486339Ssam goto bad; 1496505Ssam } 15010146Ssam /* don't allow broadcast messages to be fragmented */ 15110146Ssam if (ip->ip_len > ifp->if_mtu) { 15210146Ssam error = EMSGSIZE; 15310146Ssam goto bad; 15410146Ssam } 1556339Ssam } 1566339Ssam 1575085Swnj /* 1584924Swnj * If small enough for interface, can just send directly. 1594545Swnj */ 1605085Swnj if (ip->ip_len <= ifp->if_mtu) { 1615085Swnj ip->ip_len = htons((u_short)ip->ip_len); 1625085Swnj ip->ip_off = htons((u_short)ip->ip_off); 1635085Swnj ip->ip_sum = 0; 1645085Swnj ip->ip_sum = in_cksum(m, hlen); 16516602Ssam error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); 1667155Swnj goto done; 1674908Swnj } 1684924Swnj 1694924Swnj /* 1704924Swnj * Too large for interface; fragment if possible. 1714924Swnj * Must be able to put at least 8 bytes per fragment. 1724924Swnj */ 1736505Ssam if (ip->ip_off & IP_DF) { 1746505Ssam error = EMSGSIZE; 1754924Swnj goto bad; 1766505Ssam } 1775085Swnj len = (ifp->if_mtu - hlen) &~ 7; 1786505Ssam if (len < 8) { 1796505Ssam error = EMSGSIZE; 1804924Swnj goto bad; 1816505Ssam } 1824924Swnj 1834924Swnj /* 1844924Swnj * Discard IP header from logical mbuf for m_copy's sake. 1854924Swnj * Loop through length of segment, make a copy of each 1864924Swnj * part and output. 1874924Swnj */ 1884924Swnj m->m_len -= sizeof (struct ip); 1894924Swnj m->m_off += sizeof (struct ip); 1905892Sroot for (off = 0; off < ip->ip_len-hlen; off += len) { 19110012Ssam struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER); 1924924Swnj struct ip *mhip; 1934924Swnj 1946505Ssam if (mh == 0) { 1956505Ssam error = ENOBUFS; 1964924Swnj goto bad; 1976505Ssam } 1984924Swnj mh->m_off = MMAXOFF - hlen; 1994924Swnj mhip = mtod(mh, struct ip *); 2004924Swnj *mhip = *ip; 2014952Swnj if (hlen > sizeof (struct ip)) { 2024924Swnj int olen = ip_optcopy(ip, mhip, off); 2034924Swnj mh->m_len = sizeof (struct ip) + olen; 2044924Swnj } else 2054924Swnj mh->m_len = sizeof (struct ip); 20627738Skarels mhip->ip_off = (off >> 3) + (ip->ip_off & ~IP_MF); 20716545Skarels if (ip->ip_off & IP_MF) 20816545Skarels mhip->ip_off |= IP_MF; 2095892Sroot if (off + len >= ip->ip_len-hlen) 2105892Sroot len = mhip->ip_len = ip->ip_len - hlen - off; 2114924Swnj else { 2124924Swnj mhip->ip_len = len; 2134924Swnj mhip->ip_off |= IP_MF; 2144496Swnj } 2155770Swnj mhip->ip_len += sizeof (struct ip); 2165770Swnj mhip->ip_len = htons((u_short)mhip->ip_len); 2174924Swnj mh->m_next = m_copy(m, off, len); 2184924Swnj if (mh->m_next == 0) { 2194967Swnj (void) m_free(mh); 2206505Ssam error = ENOBUFS; /* ??? */ 2214924Swnj goto bad; 2224674Swnj } 2235892Sroot mhip->ip_off = htons((u_short)mhip->ip_off); 2245892Sroot mhip->ip_sum = 0; 2255892Sroot mhip->ip_sum = in_cksum(mh, hlen); 22616602Ssam if (error = (*ifp->if_output)(ifp, mh, (struct sockaddr *)dst)) 2276505Ssam break; 2284924Swnj } 2294924Swnj bad: 2304924Swnj m_freem(m); 2317155Swnj done: 23212417Ssam if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) 2337155Swnj RTFREE(ro->ro_rt); 2346505Ssam return (error); 2354924Swnj } 2364924Swnj 2374924Swnj /* 23824814Skarels * Insert IP options into preformed packet. 23924814Skarels * Adjust IP destination as required for IP source routing, 24024814Skarels * as indicated by a non-zero in_addr at the start of the options. 24124814Skarels */ 24224814Skarels struct mbuf * 24324814Skarels ip_insertoptions(m, opt, phlen) 24424814Skarels register struct mbuf *m; 24524814Skarels struct mbuf *opt; 24624814Skarels int *phlen; 24724814Skarels { 24824814Skarels register struct ipoption *p = mtod(opt, struct ipoption *); 24924814Skarels struct mbuf *n; 25024814Skarels register struct ip *ip = mtod(m, struct ip *); 25126385Skarels unsigned optlen; 25224814Skarels 25324814Skarels optlen = opt->m_len - sizeof(p->ipopt_dst); 25424814Skarels if (p->ipopt_dst.s_addr) 25524814Skarels ip->ip_dst = p->ipopt_dst; 25624814Skarels if (m->m_off >= MMAXOFF || MMINOFF + optlen > m->m_off) { 25724814Skarels MGET(n, M_DONTWAIT, MT_HEADER); 25824814Skarels if (n == 0) 25924814Skarels return (m); 26024814Skarels m->m_len -= sizeof(struct ip); 26124814Skarels m->m_off += sizeof(struct ip); 26224814Skarels n->m_next = m; 26324814Skarels m = n; 26424814Skarels m->m_off = MMAXOFF - sizeof(struct ip) - optlen; 26524814Skarels m->m_len = optlen + sizeof(struct ip); 26624814Skarels bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 26724814Skarels } else { 26824814Skarels m->m_off -= optlen; 26924814Skarels m->m_len += optlen; 27024814Skarels ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 27124814Skarels } 27224814Skarels ip = mtod(m, struct ip *); 27326385Skarels bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen); 27424814Skarels *phlen = sizeof(struct ip) + optlen; 27524814Skarels ip->ip_len += optlen; 27624814Skarels return (m); 27724814Skarels } 27824814Skarels 27924814Skarels /* 2804924Swnj * Copy options from ip to jp. 2814952Swnj * If off is 0 all options are copied 2824924Swnj * otherwise copy selectively. 2834924Swnj */ 2844924Swnj ip_optcopy(ip, jp, off) 2854924Swnj struct ip *ip, *jp; 2864924Swnj int off; 2874924Swnj { 2884924Swnj register u_char *cp, *dp; 2894924Swnj int opt, optlen, cnt; 2904924Swnj 2914924Swnj cp = (u_char *)(ip + 1); 2924924Swnj dp = (u_char *)(jp + 1); 2934924Swnj cnt = (ip->ip_hl << 2) - sizeof (struct ip); 2944924Swnj for (; cnt > 0; cnt -= optlen, cp += optlen) { 2954924Swnj opt = cp[0]; 2964924Swnj if (opt == IPOPT_EOL) 2974924Swnj break; 2984924Swnj if (opt == IPOPT_NOP) 2994924Swnj optlen = 1; 3004924Swnj else 30124814Skarels optlen = cp[IPOPT_OLEN]; 3024924Swnj if (optlen > cnt) /* XXX */ 3034924Swnj optlen = cnt; /* XXX */ 3044924Swnj if (off == 0 || IPOPT_COPIED(opt)) { 3054952Swnj bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen); 3064924Swnj dp += optlen; 3074674Swnj } 3084545Swnj } 3094924Swnj for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) 3104924Swnj *dp++ = IPOPT_EOL; 3114924Swnj return (optlen); 3124496Swnj } 31324814Skarels 31424814Skarels /* 31524814Skarels * IP socket option processing. 31624814Skarels */ 31724814Skarels ip_ctloutput(op, so, level, optname, m) 31824814Skarels int op; 31924814Skarels struct socket *so; 32024814Skarels int level, optname; 32124814Skarels struct mbuf **m; 32224814Skarels { 32324814Skarels int error = 0; 32424814Skarels struct inpcb *inp = sotoinpcb(so); 32524814Skarels 32624814Skarels if (level != IPPROTO_IP) 32724814Skarels error = EINVAL; 32824814Skarels else switch (op) { 32924814Skarels 33024814Skarels case PRCO_SETOPT: 33124814Skarels switch (optname) { 33224814Skarels case IP_OPTIONS: 33326036Skarels return (ip_pcbopts(&inp->inp_options, *m)); 33424814Skarels 33524814Skarels default: 33624814Skarels error = EINVAL; 33724814Skarels break; 33824814Skarels } 33924814Skarels break; 34024814Skarels 34124814Skarels case PRCO_GETOPT: 34224814Skarels switch (optname) { 34324814Skarels case IP_OPTIONS: 34424814Skarels *m = m_get(M_WAIT, MT_SOOPTS); 34524814Skarels if (inp->inp_options) { 34624814Skarels (*m)->m_off = inp->inp_options->m_off; 34724814Skarels (*m)->m_len = inp->inp_options->m_len; 34824814Skarels bcopy(mtod(inp->inp_options, caddr_t), 34926385Skarels mtod(*m, caddr_t), (unsigned)(*m)->m_len); 35024814Skarels } else 35124814Skarels (*m)->m_len = 0; 35224814Skarels break; 35324814Skarels default: 35424814Skarels error = EINVAL; 35524814Skarels break; 35624814Skarels } 35724814Skarels break; 35824814Skarels } 35931649Smckusick if (op == PRCO_SETOPT && *m) 36026385Skarels (void)m_free(*m); 36124814Skarels return (error); 36224814Skarels } 36324814Skarels 36424814Skarels /* 36526036Skarels * Set up IP options in pcb for insertion in output packets. 36626036Skarels * Store in mbuf with pointer in pcbopt, adding pseudo-option 36726036Skarels * with destination address if source routed. 36824814Skarels */ 36926036Skarels ip_pcbopts(pcbopt, m) 37026036Skarels struct mbuf **pcbopt; 37126036Skarels register struct mbuf *m; 37224814Skarels { 37324814Skarels register cnt, optlen; 37424814Skarels register u_char *cp; 37524814Skarels u_char opt; 37624814Skarels 37724814Skarels /* turn off any old options */ 37826036Skarels if (*pcbopt) 37926385Skarels (void)m_free(*pcbopt); 38026036Skarels *pcbopt = 0; 38124814Skarels if (m == (struct mbuf *)0 || m->m_len == 0) { 38224814Skarels /* 38324814Skarels * Only turning off any previous options. 38424814Skarels */ 38524814Skarels if (m) 38626385Skarels (void)m_free(m); 38724814Skarels return (0); 38824814Skarels } 38924814Skarels 39024814Skarels #ifndef vax 39124814Skarels if (m->m_len % sizeof(long)) 39224814Skarels goto bad; 39324814Skarels #endif 39424814Skarels /* 39524814Skarels * IP first-hop destination address will be stored before 39624814Skarels * actual options; move other options back 39724814Skarels * and clear it when none present. 39824814Skarels */ 39924814Skarels #if MAX_IPOPTLEN >= MMAXOFF - MMINOFF 40024814Skarels if (m->m_off + m->m_len + sizeof(struct in_addr) > MAX_IPOPTLEN) 40124814Skarels goto bad; 40224814Skarels #else 40324814Skarels if (m->m_off + m->m_len + sizeof(struct in_addr) > MMAXOFF) 40424814Skarels goto bad; 40524814Skarels #endif 40624814Skarels cnt = m->m_len; 40724814Skarels m->m_len += sizeof(struct in_addr); 40824814Skarels cp = mtod(m, u_char *) + sizeof(struct in_addr); 40926385Skarels ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt); 41024814Skarels bzero(mtod(m, caddr_t), sizeof(struct in_addr)); 41124814Skarels 41224814Skarels for (; cnt > 0; cnt -= optlen, cp += optlen) { 41324814Skarels opt = cp[IPOPT_OPTVAL]; 41424814Skarels if (opt == IPOPT_EOL) 41524814Skarels break; 41624814Skarels if (opt == IPOPT_NOP) 41724814Skarels optlen = 1; 41824814Skarels else { 41924814Skarels optlen = cp[IPOPT_OLEN]; 42024814Skarels if (optlen <= IPOPT_OLEN || optlen > cnt) 42124814Skarels goto bad; 42224814Skarels } 42324814Skarels switch (opt) { 42424814Skarels 42524814Skarels default: 42624814Skarels break; 42724814Skarels 42824814Skarels case IPOPT_LSRR: 42924814Skarels case IPOPT_SSRR: 43024814Skarels /* 43124814Skarels * user process specifies route as: 43224814Skarels * ->A->B->C->D 43324814Skarels * D must be our final destination (but we can't 43424814Skarels * check that since we may not have connected yet). 43524814Skarels * A is first hop destination, which doesn't appear in 43624814Skarels * actual IP option, but is stored before the options. 43724814Skarels */ 43824814Skarels if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) 43924814Skarels goto bad; 44024814Skarels m->m_len -= sizeof(struct in_addr); 44124814Skarels cnt -= sizeof(struct in_addr); 44224814Skarels optlen -= sizeof(struct in_addr); 44324814Skarels cp[IPOPT_OLEN] = optlen; 44424814Skarels /* 44524814Skarels * Move first hop before start of options. 44624814Skarels */ 44726385Skarels bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), 44824814Skarels sizeof(struct in_addr)); 44924814Skarels /* 45024814Skarels * Then copy rest of options back 45124814Skarels * to close up the deleted entry. 45224814Skarels */ 45326385Skarels ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] + 45426385Skarels sizeof(struct in_addr)), 45526385Skarels (caddr_t)&cp[IPOPT_OFFSET+1], 45626385Skarels (unsigned)cnt + sizeof(struct in_addr)); 45724814Skarels break; 45824814Skarels } 45924814Skarels } 46026036Skarels *pcbopt = m; 46124814Skarels return (0); 46224814Skarels 46324814Skarels bad: 46426385Skarels (void)m_free(m); 46524814Skarels return (EINVAL); 46624814Skarels } 467