Commit | Line | Data |
---|---|---|
5afc289d | 1 | /* |
76ea132e | 2 | * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California. |
2b6b6284 | 3 | * All rights reserved. |
5afc289d | 4 | * |
dbf0c423 | 5 | * %sccs.include.redist.c% |
2b6b6284 | 6 | * |
6f908ea9 | 7 | * @(#)ip_output.c 7.23 (Berkeley) %G% |
5afc289d | 8 | */ |
6e8b2eca | 9 | |
20666ad3 | 10 | #include "param.h" |
d1f75e79 | 11 | #include "malloc.h" |
20666ad3 JB |
12 | #include "mbuf.h" |
13 | #include "errno.h" | |
53ba4464 | 14 | #include "protosw.h" |
20666ad3 JB |
15 | #include "socket.h" |
16 | #include "socketvar.h" | |
f4d55810 SL |
17 | |
18 | #include "../net/if.h" | |
19 | #include "../net/route.h" | |
20 | ||
20666ad3 JB |
21 | #include "in.h" |
22 | #include "in_systm.h" | |
23 | #include "ip.h" | |
60c0bb09 KS |
24 | #include "in_pcb.h" |
25 | #include "in_var.h" | |
20666ad3 | 26 | #include "ip_var.h" |
f4d55810 | 27 | |
b152d3c4 | 28 | #ifdef vax |
c18e27ed | 29 | #include "machine/mtpr.h" |
b152d3c4 | 30 | #endif |
ac904f92 | 31 | |
53ba4464 MK |
32 | struct mbuf *ip_insertoptions(); |
33 | ||
34 | /* | |
35 | * IP output. The packet in mbuf chain m contains a skeletal IP | |
99fe25ae MK |
36 | * header (with len, off, ttl, proto, tos, src, dst). |
37 | * The mbuf chain containing the packet will be freed. | |
38 | * The mbuf opt, if present, will not be freed. | |
53ba4464 | 39 | */ |
07136d15 MK |
40 | ip_output(m0, opt, ro, flags) |
41 | struct mbuf *m0; | |
8a13b737 | 42 | struct mbuf *opt; |
ee787340 | 43 | struct route *ro; |
0e3f761f | 44 | int flags; |
ac904f92 | 45 | { |
07136d15 | 46 | register struct ip *ip, *mhip; |
8a13b737 | 47 | register struct ifnet *ifp; |
07136d15 MK |
48 | register struct mbuf *m = m0; |
49 | register int hlen = sizeof (struct ip); | |
50 | int len, off, error = 0; | |
ee787340 | 51 | struct route iproute; |
a8671e7e | 52 | struct sockaddr_in *dst; |
60c0bb09 | 53 | struct in_ifaddr *ia; |
ac904f92 | 54 | |
73112d5c MK |
55 | #ifdef DIAGNOSTIC |
56 | if ((m->m_flags & M_PKTHDR) == 0) | |
57 | panic("ip_output no HDR"); | |
58 | #endif | |
07136d15 MK |
59 | if (opt) { |
60 | m = ip_insertoptions(m, opt, &len); | |
61 | hlen = len; | |
62 | } | |
53ba4464 | 63 | ip = mtod(m, struct ip *); |
e8d11875 | 64 | /* |
2b4b57cd | 65 | * Fill in IP header. |
e8d11875 | 66 | */ |
0e3f761f SL |
67 | if ((flags & IP_FORWARDING) == 0) { |
68 | ip->ip_v = IPVERSION; | |
69 | ip->ip_off &= IP_DF; | |
70 | ip->ip_id = htons(ip_id++); | |
4adcd589 | 71 | ip->ip_hl = hlen >> 2; |
ea9a9897 | 72 | } else { |
53ba4464 | 73 | hlen = ip->ip_hl << 2; |
ea9a9897 KS |
74 | ipstat.ips_localout++; |
75 | } | |
8a13b737 | 76 | /* |
a13c006d | 77 | * Route packet. |
8a13b737 | 78 | */ |
ee787340 SL |
79 | if (ro == 0) { |
80 | ro = &iproute; | |
81 | bzero((caddr_t)ro, sizeof (*ro)); | |
82 | } | |
a8671e7e | 83 | dst = (struct sockaddr_in *)&ro->ro_dst; |
ccb87262 MK |
84 | /* |
85 | * If there is a cached route, | |
86 | * check that it is to the same destination | |
87 | * and is still up. If not, free it and try again. | |
88 | */ | |
89 | if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || | |
90 | dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { | |
91 | RTFREE(ro->ro_rt); | |
92 | ro->ro_rt = (struct rtentry *)0; | |
93 | } | |
ee787340 | 94 | if (ro->ro_rt == 0) { |
a8671e7e | 95 | dst->sin_family = AF_INET; |
d1f75e79 | 96 | dst->sin_len = sizeof(*dst); |
a8671e7e | 97 | dst->sin_addr = ip->ip_dst; |
d55475b1 MK |
98 | } |
99 | /* | |
100 | * If routing to interface only, | |
101 | * short circuit routing lookup. | |
102 | */ | |
103 | if (flags & IP_ROUTETOIF) { | |
52c6a991 | 104 | |
9340d736 | 105 | ia = (struct in_ifaddr *)ifa_ifwithdstaddr((struct sockaddr *)dst); |
52c6a991 MK |
106 | if (ia == 0) |
107 | ia = in_iaonnetof(in_netof(ip->ip_dst)); | |
d55475b1 MK |
108 | if (ia == 0) { |
109 | error = ENETUNREACH; | |
110 | goto bad; | |
a13c006d | 111 | } |
d55475b1 MK |
112 | ifp = ia->ia_ifp; |
113 | } else { | |
d55475b1 MK |
114 | if (ro->ro_rt == 0) |
115 | rtalloc(ro); | |
76ea132e MK |
116 | if (ro->ro_rt == 0) { |
117 | error = EHOSTUNREACH; | |
d55475b1 MK |
118 | goto bad; |
119 | } | |
60c0bb09 | 120 | ia = (struct in_ifaddr *)ro->ro_rt->rt_ifa; |
76ea132e | 121 | ifp = ro->ro_rt->rt_ifp; |
d55475b1 | 122 | ro->ro_rt->rt_use++; |
39e4cc50 | 123 | if (ro->ro_rt->rt_flags & RTF_GATEWAY) |
d1f75e79 | 124 | dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; |
0e3f761f | 125 | } |
5afc289d MK |
126 | #ifndef notdef |
127 | /* | |
128 | * If source address not specified yet, use address | |
129 | * of outgoing interface. | |
130 | */ | |
60c0bb09 KS |
131 | if (ip->ip_src.s_addr == INADDR_ANY) |
132 | ip->ip_src = IA_SIN(ia)->sin_addr; | |
5afc289d | 133 | #endif |
a13c006d | 134 | /* |
af099287 SL |
135 | * Look for broadcast address and |
136 | * and verify user is allowed to send | |
19f96414 | 137 | * such a packet. |
a13c006d | 138 | */ |
dfce8240 | 139 | if (in_broadcast(dst->sin_addr)) { |
19f96414 SL |
140 | if ((ifp->if_flags & IFF_BROADCAST) == 0) { |
141 | error = EADDRNOTAVAIL; | |
142 | goto bad; | |
143 | } | |
0e3f761f | 144 | if ((flags & IP_ALLOWBROADCAST) == 0) { |
a13c006d | 145 | error = EACCES; |
ee787340 | 146 | goto bad; |
8a2f82db | 147 | } |
19f96414 | 148 | /* don't allow broadcast messages to be fragmented */ |
5c82f140 | 149 | if ((u_short)ip->ip_len > ifp->if_mtu) { |
19f96414 SL |
150 | error = EMSGSIZE; |
151 | goto bad; | |
152 | } | |
d1f75e79 | 153 | m->m_flags |= M_BCAST; |
ee787340 | 154 | } |
ac904f92 | 155 | |
2b4b57cd BJ |
156 | /* |
157 | * If small enough for interface, can just send directly. | |
158 | */ | |
5c82f140 | 159 | if ((u_short)ip->ip_len <= ifp->if_mtu) { |
8a13b737 BJ |
160 | ip->ip_len = htons((u_short)ip->ip_len); |
161 | ip->ip_off = htons((u_short)ip->ip_off); | |
162 | ip->ip_sum = 0; | |
163 | ip->ip_sum = in_cksum(m, hlen); | |
60c0bb09 KS |
164 | error = (*ifp->if_output)(ifp, m, |
165 | (struct sockaddr *)dst, ro->ro_rt); | |
a13c006d | 166 | goto done; |
cdad2eb1 | 167 | } |
ea9a9897 | 168 | ipstat.ips_fragmented++; |
2b4b57cd BJ |
169 | /* |
170 | * Too large for interface; fragment if possible. | |
171 | * Must be able to put at least 8 bytes per fragment. | |
172 | */ | |
8a2f82db SL |
173 | if (ip->ip_off & IP_DF) { |
174 | error = EMSGSIZE; | |
2b4b57cd | 175 | goto bad; |
8a2f82db | 176 | } |
8a13b737 | 177 | len = (ifp->if_mtu - hlen) &~ 7; |
8a2f82db SL |
178 | if (len < 8) { |
179 | error = EMSGSIZE; | |
2b4b57cd | 180 | goto bad; |
8a2f82db | 181 | } |
2b4b57cd | 182 | |
e537fed1 MK |
183 | { |
184 | int mhlen, firstlen = len; | |
d1f75e79 | 185 | struct mbuf **mnext = &m->m_nextpkt; |
e537fed1 | 186 | |
2b4b57cd | 187 | /* |
e537fed1 MK |
188 | * Loop through length of segment after first fragment, |
189 | * make new header and copy data of each part and link onto chain. | |
2b4b57cd | 190 | */ |
07136d15 | 191 | m0 = m; |
e537fed1 | 192 | mhlen = sizeof (struct ip); |
5c82f140 | 193 | for (off = hlen + len; off < (u_short)ip->ip_len; off += len) { |
d1f75e79 | 194 | MGETHDR(m, M_DONTWAIT, MT_HEADER); |
07136d15 | 195 | if (m == 0) { |
8a2f82db | 196 | error = ENOBUFS; |
f688491d | 197 | goto sendorfree; |
8a2f82db | 198 | } |
d1f75e79 | 199 | m->m_data += max_linkhdr; |
07136d15 | 200 | mhip = mtod(m, struct ip *); |
2b4b57cd | 201 | *mhip = *ip; |
4ad99bae | 202 | if (hlen > sizeof (struct ip)) { |
e537fed1 | 203 | mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); |
07136d15 | 204 | mhip->ip_hl = mhlen >> 2; |
e537fed1 | 205 | } |
07136d15 | 206 | m->m_len = mhlen; |
e537fed1 | 207 | mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); |
4adcd589 MK |
208 | if (ip->ip_off & IP_MF) |
209 | mhip->ip_off |= IP_MF; | |
5c82f140 MK |
210 | if (off + len >= (u_short)ip->ip_len) |
211 | len = (u_short)ip->ip_len - off; | |
07136d15 | 212 | else |
2b4b57cd | 213 | mhip->ip_off |= IP_MF; |
07136d15 MK |
214 | mhip->ip_len = htons((u_short)(len + mhlen)); |
215 | m->m_next = m_copy(m0, off, len); | |
216 | if (m->m_next == 0) { | |
8a2f82db | 217 | error = ENOBUFS; /* ??? */ |
e537fed1 | 218 | goto sendorfree; |
98444525 | 219 | } |
d1f75e79 MK |
220 | m->m_pkthdr.len = mhlen + len; |
221 | m->m_pkthdr.rcvif = (struct ifnet *)0; | |
0b49870f | 222 | mhip->ip_off = htons((u_short)mhip->ip_off); |
0b49870f | 223 | mhip->ip_sum = 0; |
07136d15 | 224 | mhip->ip_sum = in_cksum(m, mhlen); |
e537fed1 | 225 | *mnext = m; |
d1f75e79 | 226 | mnext = &m->m_nextpkt; |
ea9a9897 | 227 | ipstat.ips_ofragments++; |
2b4b57cd | 228 | } |
e537fed1 MK |
229 | /* |
230 | * Update first fragment by trimming what's been copied out | |
231 | * and updating header, then send each fragment (in order). | |
232 | */ | |
9d14dd83 | 233 | m = m0; |
5c82f140 | 234 | m_adj(m, hlen + firstlen - (u_short)ip->ip_len); |
d1f75e79 MK |
235 | m->m_pkthdr.len = hlen + firstlen; |
236 | ip->ip_len = htons((u_short)m->m_pkthdr.len); | |
4ee97dd8 | 237 | ip->ip_off = htons((u_short)(ip->ip_off | IP_MF)); |
e537fed1 | 238 | ip->ip_sum = 0; |
9d14dd83 | 239 | ip->ip_sum = in_cksum(m, hlen); |
e537fed1 MK |
240 | sendorfree: |
241 | for (m = m0; m; m = m0) { | |
d1f75e79 MK |
242 | m0 = m->m_nextpkt; |
243 | m->m_nextpkt = 0; | |
e537fed1 MK |
244 | if (error == 0) |
245 | error = (*ifp->if_output)(ifp, m, | |
60c0bb09 | 246 | (struct sockaddr *)dst, ro->ro_rt); |
e537fed1 MK |
247 | else |
248 | m_freem(m); | |
249 | } | |
250 | } | |
a13c006d | 251 | done: |
0e3f761f | 252 | if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) |
a13c006d | 253 | RTFREE(ro->ro_rt); |
8a2f82db | 254 | return (error); |
e537fed1 MK |
255 | bad: |
256 | m_freem(m0); | |
257 | goto done; | |
2b4b57cd BJ |
258 | } |
259 | ||
53ba4464 MK |
260 | /* |
261 | * Insert IP options into preformed packet. | |
262 | * Adjust IP destination as required for IP source routing, | |
263 | * as indicated by a non-zero in_addr at the start of the options. | |
264 | */ | |
265 | struct mbuf * | |
266 | ip_insertoptions(m, opt, phlen) | |
267 | register struct mbuf *m; | |
268 | struct mbuf *opt; | |
269 | int *phlen; | |
270 | { | |
271 | register struct ipoption *p = mtod(opt, struct ipoption *); | |
272 | struct mbuf *n; | |
273 | register struct ip *ip = mtod(m, struct ip *); | |
8011f5df | 274 | unsigned optlen; |
53ba4464 MK |
275 | |
276 | optlen = opt->m_len - sizeof(p->ipopt_dst); | |
5c82f140 MK |
277 | if (optlen + (u_short)ip->ip_len > IP_MAXPACKET) |
278 | return (m); /* XXX should fail */ | |
53ba4464 MK |
279 | if (p->ipopt_dst.s_addr) |
280 | ip->ip_dst = p->ipopt_dst; | |
d1f75e79 MK |
281 | if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) { |
282 | MGETHDR(n, M_DONTWAIT, MT_HEADER); | |
53ba4464 MK |
283 | if (n == 0) |
284 | return (m); | |
d1f75e79 | 285 | n->m_pkthdr.len = m->m_pkthdr.len + optlen; |
53ba4464 | 286 | m->m_len -= sizeof(struct ip); |
d1f75e79 | 287 | m->m_data += sizeof(struct ip); |
53ba4464 MK |
288 | n->m_next = m; |
289 | m = n; | |
53ba4464 | 290 | m->m_len = optlen + sizeof(struct ip); |
d1f75e79 | 291 | m->m_data += max_linkhdr; |
53ba4464 MK |
292 | bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); |
293 | } else { | |
d1f75e79 | 294 | m->m_data -= optlen; |
53ba4464 | 295 | m->m_len += optlen; |
d1f75e79 | 296 | m->m_pkthdr.len += optlen; |
53ba4464 MK |
297 | ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); |
298 | } | |
299 | ip = mtod(m, struct ip *); | |
8011f5df | 300 | bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen); |
53ba4464 MK |
301 | *phlen = sizeof(struct ip) + optlen; |
302 | ip->ip_len += optlen; | |
303 | return (m); | |
304 | } | |
305 | ||
2b4b57cd | 306 | /* |
e537fed1 MK |
307 | * Copy options from ip to jp, |
308 | * omitting those not copied during fragmentation. | |
2b4b57cd | 309 | */ |
e537fed1 | 310 | ip_optcopy(ip, jp) |
2b4b57cd | 311 | struct ip *ip, *jp; |
2b4b57cd BJ |
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 | |
53ba4464 | 326 | optlen = cp[IPOPT_OLEN]; |
e537fed1 MK |
327 | /* bogus lengths should have been caught by ip_dooptions */ |
328 | if (optlen > cnt) | |
329 | optlen = cnt; | |
330 | if (IPOPT_COPIED(opt)) { | |
4ad99bae | 331 | bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen); |
2b4b57cd | 332 | dp += optlen; |
ac904f92 | 333 | } |
e8d11875 | 334 | } |
2b4b57cd BJ |
335 | for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) |
336 | *dp++ = IPOPT_EOL; | |
337 | return (optlen); | |
ac904f92 | 338 | } |
53ba4464 MK |
339 | |
340 | /* | |
341 | * IP socket option processing. | |
342 | */ | |
76ea132e | 343 | ip_ctloutput(op, so, level, optname, mp) |
53ba4464 MK |
344 | int op; |
345 | struct socket *so; | |
346 | int level, optname; | |
76ea132e | 347 | struct mbuf **mp; |
53ba4464 | 348 | { |
76ea132e MK |
349 | register struct inpcb *inp = sotoinpcb(so); |
350 | register struct mbuf *m = *mp; | |
351 | register int optval; | |
53ba4464 | 352 | int error = 0; |
53ba4464 MK |
353 | |
354 | if (level != IPPROTO_IP) | |
355 | error = EINVAL; | |
356 | else switch (op) { | |
357 | ||
358 | case PRCO_SETOPT: | |
359 | switch (optname) { | |
360 | case IP_OPTIONS: | |
73112d5c | 361 | #ifdef notyet |
76ea132e MK |
362 | case IP_RETOPTS: |
363 | return (ip_pcbopts(optname, &inp->inp_options, m)); | |
73112d5c MK |
364 | #else |
365 | return (ip_pcbopts(&inp->inp_options, m)); | |
366 | #endif | |
76ea132e MK |
367 | |
368 | case IP_TOS: | |
369 | case IP_TTL: | |
370 | case IP_RECVOPTS: | |
371 | case IP_RECVRETOPTS: | |
372 | case IP_RECVDSTADDR: | |
373 | if (m->m_len != sizeof(int)) | |
374 | error = EINVAL; | |
375 | else { | |
376 | optval = *mtod(m, int *); | |
73112d5c | 377 | switch (optname) { |
76ea132e MK |
378 | |
379 | case IP_TOS: | |
380 | inp->inp_ip.ip_tos = optval; | |
381 | break; | |
382 | ||
383 | case IP_TTL: | |
6f908ea9 | 384 | inp->inp_ip.ip_ttl = optval; |
76ea132e MK |
385 | break; |
386 | #define OPTSET(bit) \ | |
387 | if (optval) \ | |
388 | inp->inp_flags |= bit; \ | |
389 | else \ | |
390 | inp->inp_flags &= ~bit; | |
391 | ||
392 | case IP_RECVOPTS: | |
393 | OPTSET(INP_RECVOPTS); | |
394 | break; | |
395 | ||
396 | case IP_RECVRETOPTS: | |
397 | OPTSET(INP_RECVRETOPTS); | |
398 | break; | |
399 | ||
400 | case IP_RECVDSTADDR: | |
401 | OPTSET(INP_RECVDSTADDR); | |
402 | break; | |
403 | } | |
404 | } | |
405 | break; | |
406 | #undef OPTSET | |
53ba4464 MK |
407 | |
408 | default: | |
409 | error = EINVAL; | |
410 | break; | |
411 | } | |
76ea132e MK |
412 | if (m) |
413 | (void)m_free(m); | |
53ba4464 MK |
414 | break; |
415 | ||
416 | case PRCO_GETOPT: | |
417 | switch (optname) { | |
418 | case IP_OPTIONS: | |
73112d5c | 419 | case IP_RETOPTS: |
76ea132e | 420 | *mp = m = m_get(M_WAIT, MT_SOOPTS); |
53ba4464 | 421 | if (inp->inp_options) { |
76ea132e | 422 | m->m_len = inp->inp_options->m_len; |
53ba4464 | 423 | bcopy(mtod(inp->inp_options, caddr_t), |
76ea132e | 424 | mtod(m, caddr_t), (unsigned)m->m_len); |
53ba4464 | 425 | } else |
76ea132e MK |
426 | m->m_len = 0; |
427 | break; | |
428 | ||
429 | case IP_TOS: | |
430 | case IP_TTL: | |
431 | case IP_RECVOPTS: | |
432 | case IP_RECVRETOPTS: | |
433 | case IP_RECVDSTADDR: | |
434 | *mp = m = m_get(M_WAIT, MT_SOOPTS); | |
435 | m->m_len = sizeof(int); | |
73112d5c | 436 | switch (optname) { |
76ea132e MK |
437 | |
438 | case IP_TOS: | |
439 | optval = inp->inp_ip.ip_tos; | |
440 | break; | |
441 | ||
442 | case IP_TTL: | |
6f908ea9 | 443 | optval = inp->inp_ip.ip_ttl; |
76ea132e MK |
444 | break; |
445 | ||
446 | #define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0) | |
447 | ||
448 | case IP_RECVOPTS: | |
449 | optval = OPTBIT(INP_RECVOPTS); | |
450 | break; | |
451 | ||
452 | case IP_RECVRETOPTS: | |
453 | optval = OPTBIT(INP_RECVRETOPTS); | |
454 | break; | |
455 | ||
456 | case IP_RECVDSTADDR: | |
457 | optval = OPTBIT(INP_RECVDSTADDR); | |
458 | break; | |
459 | } | |
460 | *mtod(m, int *) = optval; | |
53ba4464 | 461 | break; |
76ea132e | 462 | |
53ba4464 MK |
463 | default: |
464 | error = EINVAL; | |
465 | break; | |
466 | } | |
467 | break; | |
468 | } | |
53ba4464 MK |
469 | return (error); |
470 | } | |
471 | ||
472 | /* | |
0c3fb1b4 MK |
473 | * Set up IP options in pcb for insertion in output packets. |
474 | * Store in mbuf with pointer in pcbopt, adding pseudo-option | |
475 | * with destination address if source routed. | |
53ba4464 | 476 | */ |
73112d5c MK |
477 | #ifdef notyet |
478 | ip_pcbopts(optname, pcbopt, m) | |
479 | int optname; | |
480 | #else | |
0c3fb1b4 | 481 | ip_pcbopts(pcbopt, m) |
73112d5c | 482 | #endif |
0c3fb1b4 MK |
483 | struct mbuf **pcbopt; |
484 | register struct mbuf *m; | |
53ba4464 MK |
485 | { |
486 | register cnt, optlen; | |
487 | register u_char *cp; | |
488 | u_char opt; | |
489 | ||
490 | /* turn off any old options */ | |
0c3fb1b4 | 491 | if (*pcbopt) |
8011f5df | 492 | (void)m_free(*pcbopt); |
0c3fb1b4 | 493 | *pcbopt = 0; |
53ba4464 MK |
494 | if (m == (struct mbuf *)0 || m->m_len == 0) { |
495 | /* | |
496 | * Only turning off any previous options. | |
497 | */ | |
498 | if (m) | |
8011f5df | 499 | (void)m_free(m); |
53ba4464 MK |
500 | return (0); |
501 | } | |
502 | ||
503 | #ifndef vax | |
504 | if (m->m_len % sizeof(long)) | |
505 | goto bad; | |
506 | #endif | |
507 | /* | |
508 | * IP first-hop destination address will be stored before | |
509 | * actual options; move other options back | |
510 | * and clear it when none present. | |
511 | */ | |
d1f75e79 | 512 | if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN]) |
53ba4464 | 513 | goto bad; |
53ba4464 MK |
514 | cnt = m->m_len; |
515 | m->m_len += sizeof(struct in_addr); | |
516 | cp = mtod(m, u_char *) + sizeof(struct in_addr); | |
8011f5df | 517 | ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt); |
53ba4464 MK |
518 | bzero(mtod(m, caddr_t), sizeof(struct in_addr)); |
519 | ||
520 | for (; cnt > 0; cnt -= optlen, cp += optlen) { | |
521 | opt = cp[IPOPT_OPTVAL]; | |
522 | if (opt == IPOPT_EOL) | |
523 | break; | |
524 | if (opt == IPOPT_NOP) | |
525 | optlen = 1; | |
526 | else { | |
527 | optlen = cp[IPOPT_OLEN]; | |
528 | if (optlen <= IPOPT_OLEN || optlen > cnt) | |
529 | goto bad; | |
530 | } | |
531 | switch (opt) { | |
532 | ||
533 | default: | |
534 | break; | |
535 | ||
536 | case IPOPT_LSRR: | |
537 | case IPOPT_SSRR: | |
538 | /* | |
539 | * user process specifies route as: | |
540 | * ->A->B->C->D | |
541 | * D must be our final destination (but we can't | |
542 | * check that since we may not have connected yet). | |
543 | * A is first hop destination, which doesn't appear in | |
544 | * actual IP option, but is stored before the options. | |
545 | */ | |
546 | if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) | |
547 | goto bad; | |
548 | m->m_len -= sizeof(struct in_addr); | |
549 | cnt -= sizeof(struct in_addr); | |
550 | optlen -= sizeof(struct in_addr); | |
551 | cp[IPOPT_OLEN] = optlen; | |
552 | /* | |
553 | * Move first hop before start of options. | |
554 | */ | |
8011f5df | 555 | bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), |
53ba4464 MK |
556 | sizeof(struct in_addr)); |
557 | /* | |
558 | * Then copy rest of options back | |
559 | * to close up the deleted entry. | |
560 | */ | |
8011f5df MK |
561 | ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] + |
562 | sizeof(struct in_addr)), | |
563 | (caddr_t)&cp[IPOPT_OFFSET+1], | |
564 | (unsigned)cnt + sizeof(struct in_addr)); | |
53ba4464 MK |
565 | break; |
566 | } | |
567 | } | |
d1f75e79 MK |
568 | if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr)) |
569 | goto bad; | |
0c3fb1b4 | 570 | *pcbopt = m; |
53ba4464 MK |
571 | return (0); |
572 | ||
573 | bad: | |
8011f5df | 574 | (void)m_free(m); |
53ba4464 MK |
575 | return (EINVAL); |
576 | } |