123745Skarels /* 234820Skarels * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 332787Sbostic * All rights reserved. 423745Skarels * 532787Sbostic * Redistribution and use in source and binary forms are permitted 634854Sbostic * provided that the above copyright notice and this paragraph are 734854Sbostic * duplicated in all such forms and that any documentation, 834854Sbostic * advertising materials, and other materials related to such 934854Sbostic * distribution and use acknowledge that the software was developed 1034854Sbostic * by the University of California, Berkeley. The name of the 1134854Sbostic * University may not be used to endorse or promote products derived 1234854Sbostic * from this software without specific prior written permission. 1334854Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434854Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534854Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1632787Sbostic * 17*40254Smckusick * @(#)ip_output.c 7.17 (Berkeley) 03/01/90 1823745Skarels */ 194571Swnj 2017061Sbloom #include "param.h" 2137318Skarels #include "malloc.h" 2217061Sbloom #include "mbuf.h" 2317061Sbloom #include "errno.h" 2424814Skarels #include "protosw.h" 2517061Sbloom #include "socket.h" 2617061Sbloom #include "socketvar.h" 2710893Ssam 2810893Ssam #include "../net/if.h" 2910893Ssam #include "../net/route.h" 3010893Ssam 3117061Sbloom #include "in.h" 3224814Skarels #include "in_pcb.h" 3317061Sbloom #include "in_systm.h" 3418375Skarels #include "in_var.h" 3517061Sbloom #include "ip.h" 3617061Sbloom #include "ip_var.h" 374496Swnj 3812460Ssam #ifdef vax 3937502Smckusick #include "machine/mtpr.h" 4012460Ssam #endif 4110893Ssam 4224814Skarels struct mbuf *ip_insertoptions(); 4324814Skarels 4424814Skarels /* 4524814Skarels * IP output. The packet in mbuf chain m contains a skeletal IP 4631037Skarels * header (with len, off, ttl, proto, tos, src, dst). 4731037Skarels * The mbuf chain containing the packet will be freed. 4831037Skarels * The mbuf opt, if present, will not be freed. 4924814Skarels */ 5033598Skarels ip_output(m0, opt, ro, flags) 5133598Skarels struct mbuf *m0; 525085Swnj struct mbuf *opt; 536339Ssam struct route *ro; 5412417Ssam int flags; 554496Swnj { 5633598Skarels register struct ip *ip, *mhip; 575085Swnj register struct ifnet *ifp; 5833598Skarels register struct mbuf *m = m0; 5933598Skarels register int hlen = sizeof (struct ip); 6033598Skarels int len, off, error = 0; 616339Ssam struct route iproute; 6216602Ssam struct sockaddr_in *dst; 634496Swnj 6437318Skarels if ((m->m_flags & M_PKTHDR) == 0) 6537318Skarels panic("ip_output no HDR"); 6633598Skarels if (opt) { 6733598Skarels m = ip_insertoptions(m, opt, &len); 6833598Skarels hlen = len; 6933598Skarels } 7024814Skarels ip = mtod(m, struct ip *); 714924Swnj /* 724924Swnj * Fill in IP header. 734924Swnj */ 7412417Ssam if ((flags & IP_FORWARDING) == 0) { 7512417Ssam ip->ip_v = IPVERSION; 7612417Ssam ip->ip_off &= IP_DF; 7712417Ssam ip->ip_id = htons(ip_id++); 7816545Skarels ip->ip_hl = hlen >> 2; 7939185Ssklower } else { 8024814Skarels hlen = ip->ip_hl << 2; 8139185Ssklower ipstat.ips_localout++; 8239185Ssklower } 834545Swnj /* 847155Swnj * Route packet. 855085Swnj */ 866339Ssam if (ro == 0) { 876339Ssam ro = &iproute; 886339Ssam bzero((caddr_t)ro, sizeof (*ro)); 895085Swnj } 9016602Ssam dst = (struct sockaddr_in *)&ro->ro_dst; 9126156Skarels /* 9226156Skarels * If there is a cached route, 9326156Skarels * check that it is to the same destination 9426156Skarels * and is still up. If not, free it and try again. 9526156Skarels */ 9626156Skarels if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 9726156Skarels dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { 9826156Skarels RTFREE(ro->ro_rt); 9926156Skarels ro->ro_rt = (struct rtentry *)0; 10026156Skarels } 1016339Ssam if (ro->ro_rt == 0) { 10216602Ssam dst->sin_family = AF_INET; 10337318Skarels dst->sin_len = sizeof(*dst); 10416602Ssam dst->sin_addr = ip->ip_dst; 10526058Skarels } 10626058Skarels /* 10726058Skarels * If routing to interface only, 10826058Skarels * short circuit routing lookup. 10926058Skarels */ 11026058Skarels if (flags & IP_ROUTETOIF) { 11126058Skarels struct in_ifaddr *ia; 11227196Skarels 11334500Skarels ia = (struct in_ifaddr *)ifa_ifwithdstaddr((struct sockaddr *)dst); 11427196Skarels if (ia == 0) 11527196Skarels ia = in_iaonnetof(in_netof(ip->ip_dst)); 11626058Skarels if (ia == 0) { 11726058Skarels error = ENETUNREACH; 11826058Skarels goto bad; 11926058Skarels } 12026058Skarels ifp = ia->ia_ifp; 12126058Skarels } else { 12226058Skarels if (ro->ro_rt == 0) 12326058Skarels rtalloc(ro); 12426058Skarels if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) { 12531200Skarels if (in_localaddr(ip->ip_dst)) 12631200Skarels error = EHOSTUNREACH; 12731200Skarels else 12831200Skarels error = ENETUNREACH; 12926058Skarels goto bad; 13026058Skarels } 13126058Skarels ro->ro_rt->rt_use++; 13230761Skarels if (ro->ro_rt->rt_flags & RTF_GATEWAY) 13337318Skarels dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; 1346339Ssam } 13523745Skarels #ifndef notdef 1367155Swnj /* 13723745Skarels * If source address not specified yet, use address 13823745Skarels * of outgoing interface. 13923745Skarels */ 14023745Skarels if (ip->ip_src.s_addr == INADDR_ANY) { 14123745Skarels register struct in_ifaddr *ia; 14223745Skarels 14323745Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 14423745Skarels if (ia->ia_ifp == ifp) { 14523745Skarels ip->ip_src = IA_SIN(ia)->sin_addr; 14623745Skarels break; 14723745Skarels } 14823745Skarels } 14923745Skarels #endif 15023745Skarels /* 15110402Ssam * Look for broadcast address and 15210402Ssam * and verify user is allowed to send 15310146Ssam * such a packet. 1547155Swnj */ 15518375Skarels if (in_broadcast(dst->sin_addr)) { 15610146Ssam if ((ifp->if_flags & IFF_BROADCAST) == 0) { 15710146Ssam error = EADDRNOTAVAIL; 15810146Ssam goto bad; 15910146Ssam } 16012417Ssam if ((flags & IP_ALLOWBROADCAST) == 0) { 1617155Swnj error = EACCES; 1626339Ssam goto bad; 1636505Ssam } 16410146Ssam /* don't allow broadcast messages to be fragmented */ 16510146Ssam if (ip->ip_len > ifp->if_mtu) { 16610146Ssam error = EMSGSIZE; 16710146Ssam goto bad; 16810146Ssam } 16937318Skarels m->m_flags |= M_BCAST; 1706339Ssam } 1716339Ssam 1725085Swnj /* 1734924Swnj * If small enough for interface, can just send directly. 1744545Swnj */ 1755085Swnj if (ip->ip_len <= ifp->if_mtu) { 1765085Swnj ip->ip_len = htons((u_short)ip->ip_len); 1775085Swnj ip->ip_off = htons((u_short)ip->ip_off); 1785085Swnj ip->ip_sum = 0; 1795085Swnj ip->ip_sum = in_cksum(m, hlen); 18016602Ssam error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); 1817155Swnj goto done; 1824908Swnj } 18339185Ssklower ipstat.ips_fragmented++; 1844924Swnj /* 1854924Swnj * Too large for interface; fragment if possible. 1864924Swnj * Must be able to put at least 8 bytes per fragment. 1874924Swnj */ 1886505Ssam if (ip->ip_off & IP_DF) { 1896505Ssam error = EMSGSIZE; 1904924Swnj goto bad; 1916505Ssam } 1925085Swnj len = (ifp->if_mtu - hlen) &~ 7; 1936505Ssam if (len < 8) { 1946505Ssam error = EMSGSIZE; 1954924Swnj goto bad; 1966505Ssam } 1974924Swnj 19833744Skarels { 19933744Skarels int mhlen, firstlen = len; 20037318Skarels struct mbuf **mnext = &m->m_nextpkt; 20133744Skarels 2024924Swnj /* 20333744Skarels * Loop through length of segment after first fragment, 20433744Skarels * make new header and copy data of each part and link onto chain. 2054924Swnj */ 20633598Skarels m0 = m; 20733744Skarels mhlen = sizeof (struct ip); 20833744Skarels for (off = hlen + len; off < ip->ip_len; off += len) { 20937318Skarels MGETHDR(m, M_DONTWAIT, MT_HEADER); 21033598Skarels if (m == 0) { 2116505Ssam error = ENOBUFS; 21234820Skarels goto sendorfree; 2136505Ssam } 21437318Skarels m->m_data += max_linkhdr; 21533598Skarels mhip = mtod(m, struct ip *); 2164924Swnj *mhip = *ip; 2174952Swnj if (hlen > sizeof (struct ip)) { 21833744Skarels mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); 21933598Skarels mhip->ip_hl = mhlen >> 2; 22033744Skarels } 22133598Skarels m->m_len = mhlen; 22233744Skarels mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); 22316545Skarels if (ip->ip_off & IP_MF) 22416545Skarels mhip->ip_off |= IP_MF; 22533744Skarels if (off + len >= ip->ip_len) 22633744Skarels len = ip->ip_len - off; 22733598Skarels else 2284924Swnj mhip->ip_off |= IP_MF; 22933598Skarels mhip->ip_len = htons((u_short)(len + mhlen)); 23033598Skarels m->m_next = m_copy(m0, off, len); 23133598Skarels if (m->m_next == 0) { 2326505Ssam error = ENOBUFS; /* ??? */ 23333744Skarels goto sendorfree; 2344674Swnj } 23537318Skarels m->m_pkthdr.len = mhlen + len; 23637318Skarels m->m_pkthdr.rcvif = (struct ifnet *)0; 2375892Sroot mhip->ip_off = htons((u_short)mhip->ip_off); 2385892Sroot mhip->ip_sum = 0; 23933598Skarels mhip->ip_sum = in_cksum(m, mhlen); 24033744Skarels *mnext = m; 24137318Skarels mnext = &m->m_nextpkt; 24239185Ssklower ipstat.ips_ofragments++; 2434924Swnj } 24433744Skarels /* 24533744Skarels * Update first fragment by trimming what's been copied out 24633744Skarels * and updating header, then send each fragment (in order). 24733744Skarels */ 248*40254Smckusick m = m0; 249*40254Smckusick m_adj(m, hlen + firstlen - ip->ip_len); 25037318Skarels m->m_pkthdr.len = hlen + firstlen; 25137318Skarels ip->ip_len = htons((u_short)m->m_pkthdr.len); 25233983Skarels ip->ip_off = htons((u_short)(ip->ip_off | IP_MF)); 25333744Skarels ip->ip_sum = 0; 254*40254Smckusick ip->ip_sum = in_cksum(m, hlen); 25533744Skarels sendorfree: 25633744Skarels for (m = m0; m; m = m0) { 25737318Skarels m0 = m->m_nextpkt; 25837318Skarels m->m_nextpkt = 0; 25933744Skarels if (error == 0) 26033744Skarels error = (*ifp->if_output)(ifp, m, 26133744Skarels (struct sockaddr *)dst); 26233744Skarels else 26333744Skarels m_freem(m); 26433744Skarels } 26533744Skarels } 2667155Swnj done: 26712417Ssam if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) 2687155Swnj RTFREE(ro->ro_rt); 2696505Ssam return (error); 27033744Skarels bad: 27133744Skarels m_freem(m0); 27233744Skarels goto done; 2734924Swnj } 2744924Swnj 2754924Swnj /* 27624814Skarels * Insert IP options into preformed packet. 27724814Skarels * Adjust IP destination as required for IP source routing, 27824814Skarels * as indicated by a non-zero in_addr at the start of the options. 27924814Skarels */ 28024814Skarels struct mbuf * 28124814Skarels ip_insertoptions(m, opt, phlen) 28224814Skarels register struct mbuf *m; 28324814Skarels struct mbuf *opt; 28424814Skarels int *phlen; 28524814Skarels { 28624814Skarels register struct ipoption *p = mtod(opt, struct ipoption *); 28724814Skarels struct mbuf *n; 28824814Skarels register struct ip *ip = mtod(m, struct ip *); 28926385Skarels unsigned optlen; 29024814Skarels 29124814Skarels optlen = opt->m_len - sizeof(p->ipopt_dst); 29224814Skarels if (p->ipopt_dst.s_addr) 29324814Skarels ip->ip_dst = p->ipopt_dst; 29437318Skarels if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) { 29537318Skarels MGETHDR(n, M_DONTWAIT, MT_HEADER); 29624814Skarels if (n == 0) 29724814Skarels return (m); 29837318Skarels n->m_pkthdr.len = m->m_pkthdr.len + optlen; 29924814Skarels m->m_len -= sizeof(struct ip); 30037318Skarels m->m_data += sizeof(struct ip); 30124814Skarels n->m_next = m; 30224814Skarels m = n; 30324814Skarels m->m_len = optlen + sizeof(struct ip); 30437318Skarels m->m_data += max_linkhdr; 30524814Skarels bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 30624814Skarels } else { 30737318Skarels m->m_data -= optlen; 30824814Skarels m->m_len += optlen; 30937318Skarels m->m_pkthdr.len += optlen; 31024814Skarels ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 31124814Skarels } 31224814Skarels ip = mtod(m, struct ip *); 31326385Skarels bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen); 31424814Skarels *phlen = sizeof(struct ip) + optlen; 31524814Skarels ip->ip_len += optlen; 31624814Skarels return (m); 31724814Skarels } 31824814Skarels 31924814Skarels /* 32033744Skarels * Copy options from ip to jp, 32133744Skarels * omitting those not copied during fragmentation. 3224924Swnj */ 32333744Skarels ip_optcopy(ip, jp) 3244924Swnj struct ip *ip, *jp; 3254924Swnj { 3264924Swnj register u_char *cp, *dp; 3274924Swnj int opt, optlen, cnt; 3284924Swnj 3294924Swnj cp = (u_char *)(ip + 1); 3304924Swnj dp = (u_char *)(jp + 1); 3314924Swnj cnt = (ip->ip_hl << 2) - sizeof (struct ip); 3324924Swnj for (; cnt > 0; cnt -= optlen, cp += optlen) { 3334924Swnj opt = cp[0]; 3344924Swnj if (opt == IPOPT_EOL) 3354924Swnj break; 3364924Swnj if (opt == IPOPT_NOP) 3374924Swnj optlen = 1; 3384924Swnj else 33924814Skarels optlen = cp[IPOPT_OLEN]; 34033744Skarels /* bogus lengths should have been caught by ip_dooptions */ 34133744Skarels if (optlen > cnt) 34233744Skarels optlen = cnt; 34333744Skarels if (IPOPT_COPIED(opt)) { 3444952Swnj bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen); 3454924Swnj dp += optlen; 3464674Swnj } 3474545Swnj } 3484924Swnj for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) 3494924Swnj *dp++ = IPOPT_EOL; 3504924Swnj return (optlen); 3514496Swnj } 35224814Skarels 35324814Skarels /* 35424814Skarels * IP socket option processing. 35524814Skarels */ 35624814Skarels ip_ctloutput(op, so, level, optname, m) 35724814Skarels int op; 35824814Skarels struct socket *so; 35924814Skarels int level, optname; 36024814Skarels struct mbuf **m; 36124814Skarels { 36224814Skarels int error = 0; 36324814Skarels struct inpcb *inp = sotoinpcb(so); 36424814Skarels 36524814Skarels if (level != IPPROTO_IP) 36624814Skarels error = EINVAL; 36724814Skarels else switch (op) { 36824814Skarels 36924814Skarels case PRCO_SETOPT: 37024814Skarels switch (optname) { 37124814Skarels case IP_OPTIONS: 37226036Skarels return (ip_pcbopts(&inp->inp_options, *m)); 37324814Skarels 37424814Skarels default: 37524814Skarels error = EINVAL; 37624814Skarels break; 37724814Skarels } 37824814Skarels break; 37924814Skarels 38024814Skarels case PRCO_GETOPT: 38124814Skarels switch (optname) { 38224814Skarels case IP_OPTIONS: 38324814Skarels *m = m_get(M_WAIT, MT_SOOPTS); 38424814Skarels if (inp->inp_options) { 38524814Skarels (*m)->m_len = inp->inp_options->m_len; 38624814Skarels bcopy(mtod(inp->inp_options, caddr_t), 38726385Skarels mtod(*m, caddr_t), (unsigned)(*m)->m_len); 38824814Skarels } else 38924814Skarels (*m)->m_len = 0; 39024814Skarels break; 39124814Skarels default: 39224814Skarels error = EINVAL; 39324814Skarels break; 39424814Skarels } 39524814Skarels break; 39624814Skarels } 39731649Smckusick if (op == PRCO_SETOPT && *m) 39826385Skarels (void)m_free(*m); 39924814Skarels return (error); 40024814Skarels } 40124814Skarels 40224814Skarels /* 40326036Skarels * Set up IP options in pcb for insertion in output packets. 40426036Skarels * Store in mbuf with pointer in pcbopt, adding pseudo-option 40526036Skarels * with destination address if source routed. 40624814Skarels */ 40726036Skarels ip_pcbopts(pcbopt, m) 40826036Skarels struct mbuf **pcbopt; 40926036Skarels register struct mbuf *m; 41024814Skarels { 41124814Skarels register cnt, optlen; 41224814Skarels register u_char *cp; 41324814Skarels u_char opt; 41424814Skarels 41524814Skarels /* turn off any old options */ 41626036Skarels if (*pcbopt) 41726385Skarels (void)m_free(*pcbopt); 41826036Skarels *pcbopt = 0; 41924814Skarels if (m == (struct mbuf *)0 || m->m_len == 0) { 42024814Skarels /* 42124814Skarels * Only turning off any previous options. 42224814Skarels */ 42324814Skarels if (m) 42426385Skarels (void)m_free(m); 42524814Skarels return (0); 42624814Skarels } 42724814Skarels 42824814Skarels #ifndef vax 42924814Skarels if (m->m_len % sizeof(long)) 43024814Skarels goto bad; 43124814Skarels #endif 43224814Skarels /* 43324814Skarels * IP first-hop destination address will be stored before 43424814Skarels * actual options; move other options back 43524814Skarels * and clear it when none present. 43624814Skarels */ 43737318Skarels if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN]) 43824814Skarels goto bad; 43924814Skarels cnt = m->m_len; 44024814Skarels m->m_len += sizeof(struct in_addr); 44124814Skarels cp = mtod(m, u_char *) + sizeof(struct in_addr); 44226385Skarels ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt); 44324814Skarels bzero(mtod(m, caddr_t), sizeof(struct in_addr)); 44424814Skarels 44524814Skarels for (; cnt > 0; cnt -= optlen, cp += optlen) { 44624814Skarels opt = cp[IPOPT_OPTVAL]; 44724814Skarels if (opt == IPOPT_EOL) 44824814Skarels break; 44924814Skarels if (opt == IPOPT_NOP) 45024814Skarels optlen = 1; 45124814Skarels else { 45224814Skarels optlen = cp[IPOPT_OLEN]; 45324814Skarels if (optlen <= IPOPT_OLEN || optlen > cnt) 45424814Skarels goto bad; 45524814Skarels } 45624814Skarels switch (opt) { 45724814Skarels 45824814Skarels default: 45924814Skarels break; 46024814Skarels 46124814Skarels case IPOPT_LSRR: 46224814Skarels case IPOPT_SSRR: 46324814Skarels /* 46424814Skarels * user process specifies route as: 46524814Skarels * ->A->B->C->D 46624814Skarels * D must be our final destination (but we can't 46724814Skarels * check that since we may not have connected yet). 46824814Skarels * A is first hop destination, which doesn't appear in 46924814Skarels * actual IP option, but is stored before the options. 47024814Skarels */ 47124814Skarels if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) 47224814Skarels goto bad; 47324814Skarels m->m_len -= sizeof(struct in_addr); 47424814Skarels cnt -= sizeof(struct in_addr); 47524814Skarels optlen -= sizeof(struct in_addr); 47624814Skarels cp[IPOPT_OLEN] = optlen; 47724814Skarels /* 47824814Skarels * Move first hop before start of options. 47924814Skarels */ 48026385Skarels bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), 48124814Skarels sizeof(struct in_addr)); 48224814Skarels /* 48324814Skarels * Then copy rest of options back 48424814Skarels * to close up the deleted entry. 48524814Skarels */ 48626385Skarels ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] + 48726385Skarels sizeof(struct in_addr)), 48826385Skarels (caddr_t)&cp[IPOPT_OFFSET+1], 48926385Skarels (unsigned)cnt + sizeof(struct in_addr)); 49024814Skarels break; 49124814Skarels } 49224814Skarels } 49337318Skarels if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr)) 49437318Skarels goto bad; 49526036Skarels *pcbopt = m; 49624814Skarels return (0); 49724814Skarels 49824814Skarels bad: 49926385Skarels (void)m_free(m); 50024814Skarels return (EINVAL); 50124814Skarels } 502