routing code now operational
[unix-history] / usr / src / sys / netinet / ip_output.c
CommitLineData
fc74f0c9 1/* ip_output.c 1.30 82/03/30 */
6e8b2eca 2
ac904f92 3#include "../h/param.h"
dad64fdf 4#include "../h/mbuf.h"
69435e93 5#include "../h/mtpr.h"
dad64fdf 6#include "../h/socket.h"
2b4b57cd 7#include "../h/socketvar.h"
8a13b737
BJ
8#include "../net/in.h"
9#include "../net/in_systm.h"
10#include "../net/if.h"
d52566dd 11#include "../net/ip.h"
eb44bfb2 12#include "../net/ip_var.h"
ee787340 13#include "../net/route.h"
ac904f92 14
ee787340 15ip_output(m, opt, ro, allowbroadcast)
2b4b57cd 16 struct mbuf *m;
8a13b737 17 struct mbuf *opt;
ee787340 18 struct route *ro;
1e977657 19 int allowbroadcast;
ac904f92 20{
2b4b57cd 21 register struct ip *ip = mtod(m, struct ip *);
8a13b737 22 register struct ifnet *ifp;
f6311fb6 23 int len, hlen = sizeof (struct ip), off;
ee787340 24 struct route iproute;
c124e997 25 struct sockaddr *dst;
ac904f92
BJ
26
27COUNT(IP_OUTPUT);
2d82c10d 28 if (opt) /* XXX */
89e6227e 29 (void) m_free(opt); /* XXX */
e8d11875 30 /*
2b4b57cd 31 * Fill in IP header.
e8d11875 32 */
2b4b57cd
BJ
33 ip->ip_v = IPVERSION;
34 ip->ip_hl = hlen >> 2;
35 ip->ip_off &= IP_DF;
8a13b737
BJ
36 ip->ip_id = htons(ip_id++);
37
38 /*
c124e997
SL
39 * Find interface for this packet in the routing
40 * table. Note each interface has placed itself
fc74f0c9
SL
41 * in there at boot time, so calls to rtalloc
42 * degenerate to if_ifonnetof(ip->ip_dst.s_net).
8a13b737 43 */
ee787340
SL
44 if (ro == 0) {
45 ro = &iproute;
46 bzero((caddr_t)ro, sizeof (*ro));
47 }
48 if (ro->ro_rt == 0) {
f6311fb6
SL
49 ro->ro_dst.sa_family = AF_INET;
50 ((struct sockaddr_in *)&ro->ro_dst)->sin_addr = ip->ip_dst;
51 rtalloc(ro);
fc74f0c9
SL
52 if (ro != &iproute)
53 ro->ro_rt->rt_refcnt++;
8a13b737 54 }
f6311fb6
SL
55 if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
56printf("no route to %x\n", ip->ip_dst.s_addr);
ee787340 57 goto bad;
f6311fb6
SL
58}
59 dst = ro->ro_rt->rt_flags&RTF_DIRECT ?
60 (struct sockaddr *)&ro->ro_dst : &ro->ro_rt->rt_gateway;
ee787340
SL
61 if (!allowbroadcast && (ifp->if_flags & IFF_BROADCAST)) {
62 struct sockaddr_in *sin;
63
64 sin = (struct sockaddr_in *)&ifp->if_broadaddr;
65 if (sin->sin_addr.s_addr == ip->ip_dst.s_addr)
66 goto bad;
67 }
ac904f92 68
2b4b57cd
BJ
69 /*
70 * If small enough for interface, can just send directly.
71 */
8a13b737 72 if (ip->ip_len <= ifp->if_mtu) {
2d82c10d 73#if vax
8a13b737
BJ
74 ip->ip_len = htons((u_short)ip->ip_len);
75 ip->ip_off = htons((u_short)ip->ip_off);
2d82c10d 76#endif
8a13b737
BJ
77 ip->ip_sum = 0;
78 ip->ip_sum = in_cksum(m, hlen);
fc74f0c9 79 ro->ro_rt->rt_use++;
c124e997 80 return ((*ifp->if_output)(ifp, m, dst));
cdad2eb1 81 }
2b4b57cd
BJ
82
83 /*
84 * Too large for interface; fragment if possible.
85 * Must be able to put at least 8 bytes per fragment.
86 */
87 if (ip->ip_off & IP_DF)
88 goto bad;
8a13b737 89 len = (ifp->if_mtu - hlen) &~ 7;
2b4b57cd
BJ
90 if (len < 8)
91 goto bad;
92
93 /*
94 * Discard IP header from logical mbuf for m_copy's sake.
95 * Loop through length of segment, make a copy of each
96 * part and output.
97 */
98 m->m_len -= sizeof (struct ip);
99 m->m_off += sizeof (struct ip);
0b49870f 100 for (off = 0; off < ip->ip_len-hlen; off += len) {
ef9b4258 101 struct mbuf *mh = m_get(M_DONTWAIT);
2b4b57cd
BJ
102 struct ip *mhip;
103
104 if (mh == 0)
105 goto bad;
106 mh->m_off = MMAXOFF - hlen;
107 mhip = mtod(mh, struct ip *);
108 *mhip = *ip;
4ad99bae 109 if (hlen > sizeof (struct ip)) {
2b4b57cd
BJ
110 int olen = ip_optcopy(ip, mhip, off);
111 mh->m_len = sizeof (struct ip) + olen;
112 } else
113 mh->m_len = sizeof (struct ip);
e8f40dc9 114 mhip->ip_off = off >> 3;
0b49870f
BJ
115 if (off + len >= ip->ip_len-hlen)
116 len = mhip->ip_len = ip->ip_len - hlen - off;
2b4b57cd
BJ
117 else {
118 mhip->ip_len = len;
119 mhip->ip_off |= IP_MF;
98444525 120 }
e8f40dc9
BJ
121 mhip->ip_len += sizeof (struct ip);
122#if vax
123 mhip->ip_len = htons((u_short)mhip->ip_len);
124#endif
2b4b57cd
BJ
125 mh->m_next = m_copy(m, off, len);
126 if (mh->m_next == 0) {
2752c877 127 (void) m_free(mh);
2b4b57cd 128 goto bad;
98444525 129 }
e8f40dc9 130#if vax
0b49870f 131 mhip->ip_off = htons((u_short)mhip->ip_off);
e8f40dc9 132#endif
0b49870f
BJ
133 mhip->ip_sum = 0;
134 mhip->ip_sum = in_cksum(mh, hlen);
fc74f0c9 135 ro->ro_rt->rt_use++;
c124e997 136 if ((*ifp->if_output)(ifp, mh, dst) == 0)
8a13b737 137 goto bad;
2b4b57cd 138 }
8a13b737
BJ
139 m_freem(m);
140 return (1);
2b4b57cd
BJ
141bad:
142 m_freem(m);
8a13b737 143 return (0);
2b4b57cd
BJ
144}
145
146/*
147 * Copy options from ip to jp.
4ad99bae 148 * If off is 0 all options are copied
2b4b57cd
BJ
149 * otherwise copy selectively.
150 */
151ip_optcopy(ip, jp, off)
152 struct ip *ip, *jp;
153 int off;
154{
155 register u_char *cp, *dp;
156 int opt, optlen, cnt;
157
4ad99bae 158COUNT(IP_OPTCOPY);
2b4b57cd
BJ
159 cp = (u_char *)(ip + 1);
160 dp = (u_char *)(jp + 1);
161 cnt = (ip->ip_hl << 2) - sizeof (struct ip);
162 for (; cnt > 0; cnt -= optlen, cp += optlen) {
163 opt = cp[0];
164 if (opt == IPOPT_EOL)
165 break;
166 if (opt == IPOPT_NOP)
167 optlen = 1;
168 else
169 optlen = cp[1];
170 if (optlen > cnt) /* XXX */
171 optlen = cnt; /* XXX */
172 if (off == 0 || IPOPT_COPIED(opt)) {
4ad99bae 173 bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
2b4b57cd 174 dp += optlen;
ac904f92 175 }
e8d11875 176 }
2b4b57cd
BJ
177 for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
178 *dp++ = IPOPT_EOL;
179 return (optlen);
ac904f92 180}