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