Annotation of 43BSDTahoe/sys/netinet/ip_output.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
        !             3:  * All rights reserved.
        !             4:  *
        !             5:  * Redistribution and use in source and binary forms are permitted
        !             6:  * provided that the above copyright notice and this paragraph are
        !             7:  * duplicated in all such forms and that any documentation,
        !             8:  * advertising materials, and other materials related to such
        !             9:  * distribution and use acknowledge that the software was developed
        !            10:  * by the University of California, Berkeley.  The name of the
        !            11:  * University may not be used to endorse or promote products derived
        !            12:  * from this software without specific prior written permission.
        !            13:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
        !            14:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
        !            15:  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
        !            16:  *
        !            17:  *     @(#)ip_output.c 7.13 (Berkeley) 6/29/88
        !            18:  */
        !            19: 
        !            20: #include "param.h"
        !            21: #include "mbuf.h"
        !            22: #include "errno.h"
        !            23: #include "protosw.h"
        !            24: #include "socket.h"
        !            25: #include "socketvar.h"
        !            26: 
        !            27: #include "../net/if.h"
        !            28: #include "../net/route.h"
        !            29: 
        !            30: #include "in.h"
        !            31: #include "in_pcb.h"
        !            32: #include "in_systm.h"
        !            33: #include "in_var.h"
        !            34: #include "ip.h"
        !            35: #include "ip_var.h"
        !            36: 
        !            37: #ifdef vax
        !            38: #include "../machine/mtpr.h"
        !            39: #endif
        !            40: 
        !            41: struct mbuf *ip_insertoptions();
        !            42: 
        !            43: /*
        !            44:  * IP output.  The packet in mbuf chain m contains a skeletal IP
        !            45:  * header (with len, off, ttl, proto, tos, src, dst).
        !            46:  * The mbuf chain containing the packet will be freed.
        !            47:  * The mbuf opt, if present, will not be freed.
        !            48:  */
        !            49: ip_output(m0, opt, ro, flags)
        !            50:        struct mbuf *m0;
        !            51:        struct mbuf *opt;
        !            52:        struct route *ro;
        !            53:        int flags;
        !            54: {
        !            55:        register struct ip *ip, *mhip;
        !            56:        register struct ifnet *ifp;
        !            57:        register struct mbuf *m = m0;
        !            58:        register int hlen = sizeof (struct ip);
        !            59:        int len, off, error = 0;
        !            60:        struct route iproute;
        !            61:        struct sockaddr_in *dst;
        !            62: 
        !            63:        if (opt) {
        !            64:                m = ip_insertoptions(m, opt, &len);
        !            65:                hlen = len;
        !            66:        }
        !            67:        ip = mtod(m, struct ip *);
        !            68:        /*
        !            69:         * Fill in IP header.
        !            70:         */
        !            71:        if ((flags & IP_FORWARDING) == 0) {
        !            72:                ip->ip_v = IPVERSION;
        !            73:                ip->ip_off &= IP_DF;
        !            74:                ip->ip_id = htons(ip_id++);
        !            75:                ip->ip_hl = hlen >> 2;
        !            76:        } else
        !            77:                hlen = ip->ip_hl << 2;
        !            78: 
        !            79:        /*
        !            80:         * Route packet.
        !            81:         */
        !            82:        if (ro == 0) {
        !            83:                ro = &iproute;
        !            84:                bzero((caddr_t)ro, sizeof (*ro));
        !            85:        }
        !            86:        dst = (struct sockaddr_in *)&ro->ro_dst;
        !            87:        /*
        !            88:         * If there is a cached route,
        !            89:         * check that it is to the same destination
        !            90:         * and is still up.  If not, free it and try again.
        !            91:         */
        !            92:        if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
        !            93:           dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {
        !            94:                RTFREE(ro->ro_rt);
        !            95:                ro->ro_rt = (struct rtentry *)0;
        !            96:        }
        !            97:        if (ro->ro_rt == 0) {
        !            98:                dst->sin_family = AF_INET;
        !            99:                dst->sin_addr = ip->ip_dst;
        !           100:        }
        !           101:        /*
        !           102:         * If routing to interface only,
        !           103:         * short circuit routing lookup.
        !           104:         */
        !           105:        if (flags & IP_ROUTETOIF) {
        !           106:                struct in_ifaddr *ia;
        !           107: 
        !           108:                ia = (struct in_ifaddr *)ifa_ifwithdstaddr((struct sockaddr *)dst);
        !           109:                if (ia == 0)
        !           110:                        ia = in_iaonnetof(in_netof(ip->ip_dst));
        !           111:                if (ia == 0) {
        !           112:                        error = ENETUNREACH;
        !           113:                        goto bad;
        !           114:                }
        !           115:                ifp = ia->ia_ifp;
        !           116:        } else {
        !           117:                if (ro->ro_rt == 0)
        !           118:                        rtalloc(ro);
        !           119:                if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
        !           120:                        if (in_localaddr(ip->ip_dst))
        !           121:                                error = EHOSTUNREACH;
        !           122:                        else
        !           123:                                error = ENETUNREACH;
        !           124:                        goto bad;
        !           125:                }
        !           126:                ro->ro_rt->rt_use++;
        !           127:                if (ro->ro_rt->rt_flags & RTF_GATEWAY)
        !           128:                        dst = (struct sockaddr_in *)&ro->ro_rt->rt_gateway;
        !           129:        }
        !           130: #ifndef notdef
        !           131:        /*
        !           132:         * If source address not specified yet, use address
        !           133:         * of outgoing interface.
        !           134:         */
        !           135:        if (ip->ip_src.s_addr == INADDR_ANY) {
        !           136:                register struct in_ifaddr *ia;
        !           137: 
        !           138:                for (ia = in_ifaddr; ia; ia = ia->ia_next)
        !           139:                        if (ia->ia_ifp == ifp) {
        !           140:                                ip->ip_src = IA_SIN(ia)->sin_addr;
        !           141:                                break;
        !           142:                        }
        !           143:        }
        !           144: #endif
        !           145:        /*
        !           146:         * Look for broadcast address and
        !           147:         * and verify user is allowed to send
        !           148:         * such a packet.
        !           149:         */
        !           150:        if (in_broadcast(dst->sin_addr)) {
        !           151:                if ((ifp->if_flags & IFF_BROADCAST) == 0) {
        !           152:                        error = EADDRNOTAVAIL;
        !           153:                        goto bad;
        !           154:                }
        !           155:                if ((flags & IP_ALLOWBROADCAST) == 0) {
        !           156:                        error = EACCES;
        !           157:                        goto bad;
        !           158:                }
        !           159:                /* don't allow broadcast messages to be fragmented */
        !           160:                if (ip->ip_len > ifp->if_mtu) {
        !           161:                        error = EMSGSIZE;
        !           162:                        goto bad;
        !           163:                }
        !           164:        }
        !           165: 
        !           166:        /*
        !           167:         * If small enough for interface, can just send directly.
        !           168:         */
        !           169:        if (ip->ip_len <= ifp->if_mtu) {
        !           170:                ip->ip_len = htons((u_short)ip->ip_len);
        !           171:                ip->ip_off = htons((u_short)ip->ip_off);
        !           172:                ip->ip_sum = 0;
        !           173:                ip->ip_sum = in_cksum(m, hlen);
        !           174:                error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst);
        !           175:                goto done;
        !           176:        }
        !           177: 
        !           178:        /*
        !           179:         * Too large for interface; fragment if possible.
        !           180:         * Must be able to put at least 8 bytes per fragment.
        !           181:         */
        !           182:        if (ip->ip_off & IP_DF) {
        !           183:                error = EMSGSIZE;
        !           184:                goto bad;
        !           185:        }
        !           186:        len = (ifp->if_mtu - hlen) &~ 7;
        !           187:        if (len < 8) {
        !           188:                error = EMSGSIZE;
        !           189:                goto bad;
        !           190:        }
        !           191: 
        !           192:     {
        !           193:        int mhlen, firstlen = len;
        !           194:        struct mbuf **mnext = &m->m_act;
        !           195: 
        !           196:        /*
        !           197:         * Loop through length of segment after first fragment,
        !           198:         * make new header and copy data of each part and link onto chain.
        !           199:         */
        !           200:        m0 = m;
        !           201:        mhlen = sizeof (struct ip);
        !           202:        for (off = hlen + len; off < ip->ip_len; off += len) {
        !           203:                MGET(m, M_DONTWAIT, MT_HEADER);
        !           204:                if (m == 0) {
        !           205:                        error = ENOBUFS;
        !           206:                        goto sendorfree;
        !           207:                }
        !           208:                m->m_off = MMAXOFF - hlen;
        !           209:                mhip = mtod(m, struct ip *);
        !           210:                *mhip = *ip;
        !           211:                if (hlen > sizeof (struct ip)) {
        !           212:                        mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
        !           213:                        mhip->ip_hl = mhlen >> 2;
        !           214:                }
        !           215:                m->m_len = mhlen;
        !           216:                mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
        !           217:                if (ip->ip_off & IP_MF)
        !           218:                        mhip->ip_off |= IP_MF;
        !           219:                if (off + len >= ip->ip_len)
        !           220:                        len = ip->ip_len - off;
        !           221:                else
        !           222:                        mhip->ip_off |= IP_MF;
        !           223:                mhip->ip_len = htons((u_short)(len + mhlen));
        !           224:                m->m_next = m_copy(m0, off, len);
        !           225:                if (m->m_next == 0) {
        !           226:                        error = ENOBUFS;        /* ??? */
        !           227:                        goto sendorfree;
        !           228:                }
        !           229:                mhip->ip_off = htons((u_short)mhip->ip_off);
        !           230:                mhip->ip_sum = 0;
        !           231:                mhip->ip_sum = in_cksum(m, mhlen);
        !           232:                *mnext = m;
        !           233:                mnext = &m->m_act;
        !           234:        }
        !           235:        /*
        !           236:         * Update first fragment by trimming what's been copied out
        !           237:         * and updating header, then send each fragment (in order).
        !           238:         */
        !           239:        m_adj(m0, hlen + firstlen - ip->ip_len);
        !           240:        ip->ip_len = htons((u_short)(hlen + firstlen));
        !           241:        ip->ip_off = htons((u_short)(ip->ip_off | IP_MF));
        !           242:        ip->ip_sum = 0;
        !           243:        ip->ip_sum = in_cksum(m0, hlen);
        !           244: sendorfree:
        !           245:        for (m = m0; m; m = m0) {
        !           246:                m0 = m->m_act;
        !           247:                m->m_act = 0;
        !           248:                if (error == 0)
        !           249:                        error = (*ifp->if_output)(ifp, m,
        !           250:                            (struct sockaddr *)dst);
        !           251:                else
        !           252:                        m_freem(m);
        !           253:        }
        !           254:     }
        !           255: done:
        !           256:        if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)
        !           257:                RTFREE(ro->ro_rt);
        !           258:        return (error);
        !           259: bad:
        !           260:        m_freem(m0);
        !           261:        goto done;
        !           262: }
        !           263: 
        !           264: /*
        !           265:  * Insert IP options into preformed packet.
        !           266:  * Adjust IP destination as required for IP source routing,
        !           267:  * as indicated by a non-zero in_addr at the start of the options.
        !           268:  */
        !           269: struct mbuf *
        !           270: ip_insertoptions(m, opt, phlen)
        !           271:        register struct mbuf *m;
        !           272:        struct mbuf *opt;
        !           273:        int *phlen;
        !           274: {
        !           275:        register struct ipoption *p = mtod(opt, struct ipoption *);
        !           276:        struct mbuf *n;
        !           277:        register struct ip *ip = mtod(m, struct ip *);
        !           278:        unsigned optlen;
        !           279: 
        !           280:        optlen = opt->m_len - sizeof(p->ipopt_dst);
        !           281:        if (p->ipopt_dst.s_addr)
        !           282:                ip->ip_dst = p->ipopt_dst;
        !           283:        if (m->m_off >= MMAXOFF || MMINOFF + optlen > m->m_off) {
        !           284:                MGET(n, M_DONTWAIT, MT_HEADER);
        !           285:                if (n == 0)
        !           286:                        return (m);
        !           287:                m->m_len -= sizeof(struct ip);
        !           288:                m->m_off += sizeof(struct ip);
        !           289:                n->m_next = m;
        !           290:                m = n;
        !           291:                m->m_off = MMAXOFF - sizeof(struct ip) - optlen;
        !           292:                m->m_len = optlen + sizeof(struct ip);
        !           293:                bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
        !           294:        } else {
        !           295:                m->m_off -= optlen;
        !           296:                m->m_len += optlen;
        !           297:                ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
        !           298:        }
        !           299:        ip = mtod(m, struct ip *);
        !           300:        bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen);
        !           301:        *phlen = sizeof(struct ip) + optlen;
        !           302:        ip->ip_len += optlen;
        !           303:        return (m);
        !           304: }
        !           305: 
        !           306: /*
        !           307:  * Copy options from ip to jp,
        !           308:  * omitting those not copied during fragmentation.
        !           309:  */
        !           310: ip_optcopy(ip, jp)
        !           311:        struct ip *ip, *jp;
        !           312: {
        !           313:        register u_char *cp, *dp;
        !           314:        int opt, optlen, cnt;
        !           315: 
        !           316:        cp = (u_char *)(ip + 1);
        !           317:        dp = (u_char *)(jp + 1);
        !           318:        cnt = (ip->ip_hl << 2) - sizeof (struct ip);
        !           319:        for (; cnt > 0; cnt -= optlen, cp += optlen) {
        !           320:                opt = cp[0];
        !           321:                if (opt == IPOPT_EOL)
        !           322:                        break;
        !           323:                if (opt == IPOPT_NOP)
        !           324:                        optlen = 1;
        !           325:                else
        !           326:                        optlen = cp[IPOPT_OLEN];
        !           327:                /* bogus lengths should have been caught by ip_dooptions */
        !           328:                if (optlen > cnt)
        !           329:                        optlen = cnt;
        !           330:                if (IPOPT_COPIED(opt)) {
        !           331:                        bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
        !           332:                        dp += optlen;
        !           333:                }
        !           334:        }
        !           335:        for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
        !           336:                *dp++ = IPOPT_EOL;
        !           337:        return (optlen);
        !           338: }
        !           339: 
        !           340: /*
        !           341:  * IP socket option processing.
        !           342:  */
        !           343: ip_ctloutput(op, so, level, optname, m)
        !           344:        int op;
        !           345:        struct socket *so;
        !           346:        int level, optname;
        !           347:        struct mbuf **m;
        !           348: {
        !           349:        int error = 0;
        !           350:        struct inpcb *inp = sotoinpcb(so);
        !           351: 
        !           352:        if (level != IPPROTO_IP)
        !           353:                error = EINVAL;
        !           354:        else switch (op) {
        !           355: 
        !           356:        case PRCO_SETOPT:
        !           357:                switch (optname) {
        !           358:                case IP_OPTIONS:
        !           359:                        return (ip_pcbopts(&inp->inp_options, *m));
        !           360: 
        !           361:                default:
        !           362:                        error = EINVAL;
        !           363:                        break;
        !           364:                }
        !           365:                break;
        !           366: 
        !           367:        case PRCO_GETOPT:
        !           368:                switch (optname) {
        !           369:                case IP_OPTIONS:
        !           370:                        *m = m_get(M_WAIT, MT_SOOPTS);
        !           371:                        if (inp->inp_options) {
        !           372:                                (*m)->m_off = inp->inp_options->m_off;
        !           373:                                (*m)->m_len = inp->inp_options->m_len;
        !           374:                                bcopy(mtod(inp->inp_options, caddr_t),
        !           375:                                    mtod(*m, caddr_t), (unsigned)(*m)->m_len);
        !           376:                        } else
        !           377:                                (*m)->m_len = 0;
        !           378:                        break;
        !           379:                default:
        !           380:                        error = EINVAL;
        !           381:                        break;
        !           382:                }
        !           383:                break;
        !           384:        }
        !           385:        if (op == PRCO_SETOPT && *m)
        !           386:                (void)m_free(*m);
        !           387:        return (error);
        !           388: }
        !           389: 
        !           390: /*
        !           391:  * Set up IP options in pcb for insertion in output packets.
        !           392:  * Store in mbuf with pointer in pcbopt, adding pseudo-option
        !           393:  * with destination address if source routed.
        !           394:  */
        !           395: ip_pcbopts(pcbopt, m)
        !           396:        struct mbuf **pcbopt;
        !           397:        register struct mbuf *m;
        !           398: {
        !           399:        register cnt, optlen;
        !           400:        register u_char *cp;
        !           401:        u_char opt;
        !           402: 
        !           403:        /* turn off any old options */
        !           404:        if (*pcbopt)
        !           405:                (void)m_free(*pcbopt);
        !           406:        *pcbopt = 0;
        !           407:        if (m == (struct mbuf *)0 || m->m_len == 0) {
        !           408:                /*
        !           409:                 * Only turning off any previous options.
        !           410:                 */
        !           411:                if (m)
        !           412:                        (void)m_free(m);
        !           413:                return (0);
        !           414:        }
        !           415: 
        !           416: #ifndef        vax
        !           417:        if (m->m_len % sizeof(long))
        !           418:                goto bad;
        !           419: #endif
        !           420:        /*
        !           421:         * IP first-hop destination address will be stored before
        !           422:         * actual options; move other options back
        !           423:         * and clear it when none present.
        !           424:         */
        !           425: #if    MAX_IPOPTLEN >= MMAXOFF - MMINOFF
        !           426:        if (m->m_off + m->m_len + sizeof(struct in_addr) > MAX_IPOPTLEN)
        !           427:                goto bad;
        !           428: #else
        !           429:        if (m->m_off + m->m_len + sizeof(struct in_addr) > MMAXOFF)
        !           430:                goto bad;
        !           431: #endif
        !           432:        cnt = m->m_len;
        !           433:        m->m_len += sizeof(struct in_addr);
        !           434:        cp = mtod(m, u_char *) + sizeof(struct in_addr);
        !           435:        ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt);
        !           436:        bzero(mtod(m, caddr_t), sizeof(struct in_addr));
        !           437: 
        !           438:        for (; cnt > 0; cnt -= optlen, cp += optlen) {
        !           439:                opt = cp[IPOPT_OPTVAL];
        !           440:                if (opt == IPOPT_EOL)
        !           441:                        break;
        !           442:                if (opt == IPOPT_NOP)
        !           443:                        optlen = 1;
        !           444:                else {
        !           445:                        optlen = cp[IPOPT_OLEN];
        !           446:                        if (optlen <= IPOPT_OLEN || optlen > cnt)
        !           447:                                goto bad;
        !           448:                }
        !           449:                switch (opt) {
        !           450: 
        !           451:                default:
        !           452:                        break;
        !           453: 
        !           454:                case IPOPT_LSRR:
        !           455:                case IPOPT_SSRR:
        !           456:                        /*
        !           457:                         * user process specifies route as:
        !           458:                         *      ->A->B->C->D
        !           459:                         * D must be our final destination (but we can't
        !           460:                         * check that since we may not have connected yet).
        !           461:                         * A is first hop destination, which doesn't appear in
        !           462:                         * actual IP option, but is stored before the options.
        !           463:                         */
        !           464:                        if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
        !           465:                                goto bad;
        !           466:                        m->m_len -= sizeof(struct in_addr);
        !           467:                        cnt -= sizeof(struct in_addr);
        !           468:                        optlen -= sizeof(struct in_addr);
        !           469:                        cp[IPOPT_OLEN] = optlen;
        !           470:                        /*
        !           471:                         * Move first hop before start of options.
        !           472:                         */
        !           473:                        bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t),
        !           474:                            sizeof(struct in_addr));
        !           475:                        /*
        !           476:                         * Then copy rest of options back
        !           477:                         * to close up the deleted entry.
        !           478:                         */
        !           479:                        ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] +
        !           480:                            sizeof(struct in_addr)),
        !           481:                            (caddr_t)&cp[IPOPT_OFFSET+1],
        !           482:                            (unsigned)cnt + sizeof(struct in_addr));
        !           483:                        break;
        !           484:                }
        !           485:        }
        !           486:        *pcbopt = m;
        !           487:        return (0);
        !           488: 
        !           489: bad:
        !           490:        (void)m_free(m);
        !           491:        return (EINVAL);
        !           492: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.