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