|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.