xref: /csrg-svn/sys/netinet/ip_output.c (revision 5584)
1 /*	ip_output.c	1.23	82/01/19	*/
2 
3 #include "../h/param.h"
4 #include "../h/mbuf.h"
5 #include "../h/mtpr.h"
6 #include "../h/socket.h"
7 #include "../h/socketvar.h"
8 #include "../net/in.h"
9 #include "../net/in_systm.h"
10 #include "../net/if.h"
11 #include "../net/ip.h"
12 #include "../net/ip_var.h"
13 
14 ip_output(m, opt)
15 	struct mbuf *m;
16 	struct mbuf *opt;
17 {
18 	register struct ip *ip = mtod(m, struct ip *);
19 	register struct ifnet *ifp;
20 	int len, hlen = sizeof (struct ip), off;
21 
22 COUNT(IP_OUTPUT);
23 	if (opt)				/* XXX */
24 		(void) m_free(opt);		/* XXX */
25 	/*
26 	 * Fill in IP header.
27 	 */
28 	ip->ip_v = IPVERSION;
29 	ip->ip_hl = hlen >> 2;
30 	ip->ip_off &= IP_DF;
31 	ip->ip_id = htons(ip_id++);
32 
33 	/*
34 	 * Find interface for this packet.
35 	 */
36 	ifp = if_ifonnetof(ip->ip_dst);
37 	if (ifp == 0) {
38 		ifp = if_gatewayfor(ip->ip_dst);
39 		if (ifp == 0)
40 			goto bad;
41 	}
42 
43 	/*
44 	 * If small enough for interface, can just send directly.
45 	 */
46 	if (ip->ip_len <= ifp->if_mtu) {
47 #if vax
48 		ip->ip_len = htons((u_short)ip->ip_len);
49 		ip->ip_off = htons((u_short)ip->ip_off);
50 #endif
51 		ip->ip_sum = 0;
52 		ip->ip_sum = in_cksum(m, hlen);
53 		return ((*ifp->if_output)(ifp, m, PF_INET));
54 	}
55 
56 	/*
57 	 * Too large for interface; fragment if possible.
58 	 * Must be able to put at least 8 bytes per fragment.
59 	 */
60 	if (ip->ip_off & IP_DF)
61 		goto bad;
62 	len = (ifp->if_mtu - hlen) &~ 7;
63 	if (len < 8)
64 		goto bad;
65 
66 	/*
67 	 * Discard IP header from logical mbuf for m_copy's sake.
68 	 * Loop through length of segment, make a copy of each
69 	 * part and output.
70 	 */
71 	m->m_len -= sizeof (struct ip);
72 	m->m_off += sizeof (struct ip);
73 	for (off = 0; off < ip->ip_len; off += len) {
74 		struct mbuf *mh = m_get(M_DONTWAIT);
75 		struct ip *mhip;
76 
77 		if (mh == 0)
78 			goto bad;
79 		mh->m_off = MMAXOFF - hlen;
80 		mhip = mtod(mh, struct ip *);
81 		*mhip = *ip;
82 		if (hlen > sizeof (struct ip)) {
83 			int olen = ip_optcopy(ip, mhip, off);
84 			mh->m_len = sizeof (struct ip) + olen;
85 		} else
86 			mh->m_len = sizeof (struct ip);
87 		mhip->ip_off = off;
88 		if (off + len >= ip->ip_len)
89 			mhip->ip_len = ip->ip_len - off;
90 		else {
91 			mhip->ip_len = len;
92 			mhip->ip_off |= IP_MF;
93 		}
94 		mhip->ip_len = htons((u_short)(mhip->ip_len + sizeof (struct ip)));
95 		mh->m_next = m_copy(m, off, len);
96 		if (mh->m_next == 0) {
97 			(void) m_free(mh);
98 			goto bad;
99 		}
100 		ip->ip_off = htons((u_short)ip->ip_off);
101 		ip->ip_sum = 0;
102 		ip->ip_sum = in_cksum(m, hlen);
103 		if ((*ifp->if_output)(ifp, mh, PF_INET) == 0)
104 			goto bad;
105 	}
106 	m_freem(m);
107 	return (1);
108 bad:
109 	m_freem(m);
110 	return (0);
111 }
112 
113 /*
114  * Copy options from ip to jp.
115  * If off is 0 all options are copied
116  * otherwise copy selectively.
117  */
118 ip_optcopy(ip, jp, off)
119 	struct ip *ip, *jp;
120 	int off;
121 {
122 	register u_char *cp, *dp;
123 	int opt, optlen, cnt;
124 
125 COUNT(IP_OPTCOPY);
126 	cp = (u_char *)(ip + 1);
127 	dp = (u_char *)(jp + 1);
128 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
129 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
130 		opt = cp[0];
131 		if (opt == IPOPT_EOL)
132 			break;
133 		if (opt == IPOPT_NOP)
134 			optlen = 1;
135 		else
136 			optlen = cp[1];
137 		if (optlen > cnt)			/* XXX */
138 			optlen = cnt;			/* XXX */
139 		if (off == 0 || IPOPT_COPIED(opt)) {
140 			bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
141 			dp += optlen;
142 		}
143 	}
144 	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
145 		*dp++ = IPOPT_EOL;
146 	return (optlen);
147 }
148