Mercurial > ecos
comparison packages/net/bsd_tcpip/current/src/sys/netinet/ip_output.c @ 208:e0c0827131d1 ecos
Merge from eCos master repository on 2002-05-20-20:11:54-BST
| author | jlarmour |
|---|---|
| date | Mon, 20 May 2002 22:19:26 +0000 |
| parents | |
| children | 56b339272cd4 |
comparison
equal
deleted
inserted
replaced
| 207:74c807ddde34 | 208:e0c0827131d1 |
|---|---|
| 1 //========================================================================== | |
| 2 // | |
| 3 // src/sys/netinet/ip_output.c | |
| 4 // | |
| 5 //========================================================================== | |
| 6 //####BSDCOPYRIGHTBEGIN#### | |
| 7 // | |
| 8 // ------------------------------------------- | |
| 9 // | |
| 10 // Portions of this software may have been derived from OpenBSD, | |
| 11 // FreeBSD or other sources, and are covered by the appropriate | |
| 12 // copyright disclaimers included herein. | |
| 13 // | |
| 14 // Portions created by Red Hat are | |
| 15 // Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. | |
| 16 // | |
| 17 // ------------------------------------------- | |
| 18 // | |
| 19 //####BSDCOPYRIGHTEND#### | |
| 20 //========================================================================== | |
| 21 | |
| 22 /* | |
| 23 * Copyright (c) 1982, 1986, 1988, 1990, 1993 | |
| 24 * The Regents of the University of California. All rights reserved. | |
| 25 * | |
| 26 * Redistribution and use in source and binary forms, with or without | |
| 27 * modification, are permitted provided that the following conditions | |
| 28 * are met: | |
| 29 * 1. Redistributions of source code must retain the above copyright | |
| 30 * notice, this list of conditions and the following disclaimer. | |
| 31 * 2. Redistributions in binary form must reproduce the above copyright | |
| 32 * notice, this list of conditions and the following disclaimer in the | |
| 33 * documentation and/or other materials provided with the distribution. | |
| 34 * 3. All advertising materials mentioning features or use of this software | |
| 35 * must display the following acknowledgement: | |
| 36 * This product includes software developed by the University of | |
| 37 * California, Berkeley and its contributors. | |
| 38 * 4. Neither the name of the University nor the names of its contributors | |
| 39 * may be used to endorse or promote products derived from this software | |
| 40 * without specific prior written permission. | |
| 41 * | |
| 42 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| 43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 45 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| 46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 52 * SUCH DAMAGE. | |
| 53 * | |
| 54 * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 | |
| 55 * $FreeBSD: src/sys/netinet/ip_output.c,v 1.99.2.16 2001/07/19 06:37:26 kris Exp $ | |
| 56 */ | |
| 57 | |
| 58 #define _IP_VHL | |
| 59 | |
| 60 #include <sys/param.h> | |
| 61 #include <sys/malloc.h> | |
| 62 #include <sys/mbuf.h> | |
| 63 #include <sys/protosw.h> | |
| 64 #include <sys/socket.h> | |
| 65 #include <sys/socketvar.h> | |
| 66 | |
| 67 #include <net/if.h> | |
| 68 #include <net/route.h> | |
| 69 | |
| 70 #include <netinet/in.h> | |
| 71 #include <netinet/in_systm.h> | |
| 72 #include <netinet/ip.h> | |
| 73 #include <netinet/in_pcb.h> | |
| 74 #include <netinet/in_var.h> | |
| 75 #include <netinet/ip_var.h> | |
| 76 | |
| 77 #ifdef IPSEC | |
| 78 #include <netinet6/ipsec.h> | |
| 79 #include <netkey/key.h> | |
| 80 #ifdef IPSEC_DEBUG | |
| 81 #include <netkey/key_debug.h> | |
| 82 #else | |
| 83 #define KEYDEBUG(lev,arg) | |
| 84 #endif | |
| 85 #endif /*IPSEC*/ | |
| 86 | |
| 87 #include <netinet/ip_fw.h> | |
| 88 | |
| 89 #ifdef DUMMYNET | |
| 90 #include <netinet/ip_dummynet.h> | |
| 91 #endif | |
| 92 | |
| 93 #ifdef IPFIREWALL_FORWARD_DEBUG | |
| 94 #define print_ip(a) printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\ | |
| 95 (ntohl(a.s_addr)>>16)&0xFF,\ | |
| 96 (ntohl(a.s_addr)>>8)&0xFF,\ | |
| 97 (ntohl(a.s_addr))&0xFF); | |
| 98 #endif | |
| 99 | |
| 100 u_short ip_id; | |
| 101 | |
| 102 static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); | |
| 103 static struct ifnet *ip_multicast_if __P((struct in_addr *, int *)); | |
| 104 static void ip_mloopback | |
| 105 __P((struct ifnet *, struct mbuf *, struct sockaddr_in *, int)); | |
| 106 static int ip_getmoptions | |
| 107 __P((struct sockopt *, struct ip_moptions *)); | |
| 108 static int ip_pcbopts __P((int, struct mbuf **, struct mbuf *)); | |
| 109 static int ip_setmoptions | |
| 110 __P((struct sockopt *, struct ip_moptions **)); | |
| 111 | |
| 112 int ip_optcopy __P((struct ip *, struct ip *)); | |
| 113 extern int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)); | |
| 114 | |
| 115 | |
| 116 extern struct protosw inetsw[]; | |
| 117 | |
| 118 /* | |
| 119 * IP output. The packet in mbuf chain m contains a skeletal IP | |
| 120 * header (with len, off, ttl, proto, tos, src, dst). | |
| 121 * The mbuf chain containing the packet will be freed. | |
| 122 * The mbuf opt, if present, will not be freed. | |
| 123 */ | |
| 124 int | |
| 125 ip_output(m0, opt, ro, flags, imo) | |
| 126 struct mbuf *m0; | |
| 127 struct mbuf *opt; | |
| 128 struct route *ro; | |
| 129 int flags; | |
| 130 struct ip_moptions *imo; | |
| 131 { | |
| 132 struct ip *ip, *mhip; | |
| 133 struct ifnet *ifp; | |
| 134 struct mbuf *m = m0; | |
| 135 int hlen = sizeof (struct ip); | |
| 136 int len, off, error = 0; | |
| 137 struct sockaddr_in *dst; | |
| 138 struct in_ifaddr *ia = NULL; | |
| 139 int isbroadcast, sw_csum; | |
| 140 #ifdef IPSEC | |
| 141 struct route iproute; | |
| 142 struct socket *so = NULL; | |
| 143 struct secpolicy *sp = NULL; | |
| 144 #endif | |
| 145 u_int16_t divert_cookie; /* firewall cookie */ | |
| 146 #ifdef IPFIREWALL_FORWARD | |
| 147 int fwd_rewrite_src = 0; | |
| 148 #endif | |
| 149 struct ip_fw_chain *rule = NULL; | |
| 150 | |
| 151 #ifdef IPDIVERT | |
| 152 /* Get and reset firewall cookie */ | |
| 153 divert_cookie = ip_divert_cookie; | |
| 154 ip_divert_cookie = 0; | |
| 155 #else | |
| 156 divert_cookie = 0; | |
| 157 #endif | |
| 158 | |
| 159 #if defined(IPFIREWALL) && defined(DUMMYNET) | |
| 160 /* | |
| 161 * dummynet packet are prepended a vestigial mbuf with | |
| 162 * m_type = MT_DUMMYNET and m_data pointing to the matching | |
| 163 * rule. | |
| 164 */ | |
| 165 if (m->m_type == MT_DUMMYNET) { | |
| 166 /* | |
| 167 * the packet was already tagged, so part of the | |
| 168 * processing was already done, and we need to go down. | |
| 169 * Get parameters from the header. | |
| 170 */ | |
| 171 rule = (struct ip_fw_chain *)(m->m_data) ; | |
| 172 opt = NULL ; | |
| 173 ro = & ( ((struct dn_pkt *)m)->ro ) ; | |
| 174 imo = NULL ; | |
| 175 dst = ((struct dn_pkt *)m)->dn_dst ; | |
| 176 ifp = ((struct dn_pkt *)m)->ifp ; | |
| 177 flags = ((struct dn_pkt *)m)->flags ; | |
| 178 | |
| 179 m0 = m = m->m_next ; | |
| 180 #ifdef IPSEC | |
| 181 so = ipsec_getsocket(m); | |
| 182 (void)ipsec_setsocket(m, NULL); | |
| 183 #endif | |
| 184 ip = mtod(m, struct ip *); | |
| 185 hlen = IP_VHL_HL(ip->ip_vhl) << 2 ; | |
| 186 if (ro->ro_rt != NULL) | |
| 187 ia = (struct in_ifaddr *)ro->ro_rt->rt_ifa; | |
| 188 goto sendit; | |
| 189 } else | |
| 190 rule = NULL ; | |
| 191 #endif | |
| 192 #ifdef IPSEC | |
| 193 so = ipsec_getsocket(m); | |
| 194 (void)ipsec_setsocket(m, NULL); | |
| 195 #endif | |
| 196 | |
| 197 #ifdef DIAGNOSTIC | |
| 198 if ((m->m_flags & M_PKTHDR) == 0) | |
| 199 panic("ip_output no HDR"); | |
| 200 if (!ro) | |
| 201 panic("ip_output no route, proto = %d", | |
| 202 mtod(m, struct ip *)->ip_p); | |
| 203 #endif | |
| 204 if (opt) { | |
| 205 m = ip_insertoptions(m, opt, &len); | |
| 206 hlen = len; | |
| 207 } | |
| 208 ip = mtod(m, struct ip *); | |
| 209 /* | |
| 210 * Fill in IP header. | |
| 211 */ | |
| 212 if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { | |
| 213 ip->ip_vhl = IP_MAKE_VHL(IPVERSION, hlen >> 2); | |
| 214 ip->ip_off &= IP_DF; | |
| 215 #ifdef RANDOM_IP_ID | |
| 216 ip->ip_id = ip_randomid(); | |
| 217 #else | |
| 218 ip->ip_id = htons(ip_id++); | |
| 219 #endif | |
| 220 ipstat.ips_localout++; | |
| 221 } else { | |
| 222 hlen = IP_VHL_HL(ip->ip_vhl) << 2; | |
| 223 } | |
| 224 | |
| 225 dst = (struct sockaddr_in *)&ro->ro_dst; | |
| 226 /* | |
| 227 * If there is a cached route, | |
| 228 * check that it is to the same destination | |
| 229 * and is still up. If not, free it and try again. | |
| 230 */ | |
| 231 if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || | |
| 232 dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { | |
| 233 RTFREE(ro->ro_rt); | |
| 234 ro->ro_rt = (struct rtentry *)0; | |
| 235 } | |
| 236 if (ro->ro_rt == 0) { | |
| 237 dst->sin_family = AF_INET; | |
| 238 dst->sin_len = sizeof(*dst); | |
| 239 dst->sin_addr = ip->ip_dst; | |
| 240 } | |
| 241 /* | |
| 242 * If routing to interface only, | |
| 243 * short circuit routing lookup. | |
| 244 */ | |
| 245 #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) | |
| 246 #define sintosa(sin) ((struct sockaddr *)(sin)) | |
| 247 if (flags & IP_ROUTETOIF) { | |
| 248 if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 && | |
| 249 (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) { | |
| 250 ipstat.ips_noroute++; | |
| 251 error = ENETUNREACH; | |
| 252 goto bad; | |
| 253 } | |
| 254 ifp = ia->ia_ifp; | |
| 255 ip->ip_ttl = 1; | |
| 256 isbroadcast = in_broadcast(dst->sin_addr, ifp); | |
| 257 } else { | |
| 258 /* | |
| 259 * If this is the case, we probably don't want to allocate | |
| 260 * a protocol-cloned route since we didn't get one from the | |
| 261 * ULP. This lets TCP do its thing, while not burdening | |
| 262 * forwarding or ICMP with the overhead of cloning a route. | |
| 263 * Of course, we still want to do any cloning requested by | |
| 264 * the link layer, as this is probably required in all cases | |
| 265 * for correct operation (as it is for ARP). | |
| 266 */ | |
| 267 if (ro->ro_rt == 0) | |
| 268 rtalloc_ign(ro, RTF_PRCLONING); | |
| 269 if (ro->ro_rt == 0) { | |
| 270 ipstat.ips_noroute++; | |
| 271 error = EHOSTUNREACH; | |
| 272 goto bad; | |
| 273 } | |
| 274 ia = ifatoia(ro->ro_rt->rt_ifa); | |
| 275 ifp = ro->ro_rt->rt_ifp; | |
| 276 ro->ro_rt->rt_use++; | |
| 277 if (ro->ro_rt->rt_flags & RTF_GATEWAY) | |
| 278 dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; | |
| 279 if (ro->ro_rt->rt_flags & RTF_HOST) | |
| 280 isbroadcast = (ro->ro_rt->rt_flags & RTF_BROADCAST); | |
| 281 else | |
| 282 isbroadcast = in_broadcast(dst->sin_addr, ifp); | |
| 283 } | |
| 284 if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { | |
| 285 struct in_multi *inm; | |
| 286 | |
| 287 m->m_flags |= M_MCAST; | |
| 288 /* | |
| 289 * IP destination address is multicast. Make sure "dst" | |
| 290 * still points to the address in "ro". (It may have been | |
| 291 * changed to point to a gateway address, above.) | |
| 292 */ | |
| 293 dst = (struct sockaddr_in *)&ro->ro_dst; | |
| 294 /* | |
| 295 * See if the caller provided any multicast options | |
| 296 */ | |
| 297 if (imo != NULL) { | |
| 298 ip->ip_ttl = imo->imo_multicast_ttl; | |
| 299 if (imo->imo_multicast_ifp != NULL) | |
| 300 ifp = imo->imo_multicast_ifp; | |
| 301 if (imo->imo_multicast_vif != -1) | |
| 302 ip->ip_src.s_addr = | |
| 303 ip_mcast_src(imo->imo_multicast_vif); | |
| 304 } else | |
| 305 ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; | |
| 306 /* | |
| 307 * Confirm that the outgoing interface supports multicast. | |
| 308 */ | |
| 309 if ((imo == NULL) || (imo->imo_multicast_vif == -1)) { | |
| 310 if ((ifp->if_flags & IFF_MULTICAST) == 0) { | |
| 311 ipstat.ips_noroute++; | |
| 312 error = ENETUNREACH; | |
| 313 goto bad; | |
| 314 } | |
| 315 } | |
| 316 /* | |
| 317 * If source address not specified yet, use address | |
| 318 * of outgoing interface. | |
| 319 */ | |
| 320 if (ip->ip_src.s_addr == INADDR_ANY) { | |
| 321 register struct in_ifaddr *ia1; | |
| 322 | |
| 323 TAILQ_FOREACH(ia1, &in_ifaddrhead, ia_link) | |
| 324 if (ia1->ia_ifp == ifp) { | |
| 325 ip->ip_src = IA_SIN(ia1)->sin_addr; | |
| 326 break; | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); | |
| 331 if (inm != NULL && | |
| 332 (imo == NULL || imo->imo_multicast_loop)) { | |
| 333 /* | |
| 334 * If we belong to the destination multicast group | |
| 335 * on the outgoing interface, and the caller did not | |
| 336 * forbid loopback, loop back a copy. | |
| 337 */ | |
| 338 ip_mloopback(ifp, m, dst, hlen); | |
| 339 } | |
| 340 else { | |
| 341 /* | |
| 342 * If we are acting as a multicast router, perform | |
| 343 * multicast forwarding as if the packet had just | |
| 344 * arrived on the interface to which we are about | |
| 345 * to send. The multicast forwarding function | |
| 346 * recursively calls this function, using the | |
| 347 * IP_FORWARDING flag to prevent infinite recursion. | |
| 348 * | |
| 349 * Multicasts that are looped back by ip_mloopback(), | |
| 350 * above, will be forwarded by the ip_input() routine, | |
| 351 * if necessary. | |
| 352 */ | |
| 353 if (ip_mrouter && (flags & IP_FORWARDING) == 0) { | |
| 354 /* | |
| 355 * Check if rsvp daemon is running. If not, don't | |
| 356 * set ip_moptions. This ensures that the packet | |
| 357 * is multicast and not just sent down one link | |
| 358 * as prescribed by rsvpd. | |
| 359 */ | |
| 360 if (!rsvp_on) | |
| 361 imo = NULL; | |
| 362 if (ip_mforward(ip, ifp, m, imo) != 0) { | |
| 363 m_freem(m); | |
| 364 goto done; | |
| 365 } | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 /* | |
| 370 * Multicasts with a time-to-live of zero may be looped- | |
| 371 * back, above, but must not be transmitted on a network. | |
| 372 * Also, multicasts addressed to the loopback interface | |
| 373 * are not sent -- the above call to ip_mloopback() will | |
| 374 * loop back a copy if this host actually belongs to the | |
| 375 * destination group on the loopback interface. | |
| 376 */ | |
| 377 if (ip->ip_ttl == 0 || ifp->if_flags & IFF_LOOPBACK) { | |
| 378 m_freem(m); | |
| 379 goto done; | |
| 380 } | |
| 381 | |
| 382 goto sendit; | |
| 383 } | |
| 384 #ifndef notdef | |
| 385 /* | |
| 386 * If source address not specified yet, use address | |
| 387 * of outgoing interface. | |
| 388 */ | |
| 389 if (ip->ip_src.s_addr == INADDR_ANY) { | |
| 390 ip->ip_src = IA_SIN(ia)->sin_addr; | |
| 391 #ifdef IPFIREWALL_FORWARD | |
| 392 /* Keep note that we did this - if the firewall changes | |
| 393 * the next-hop, our interface may change, changing the | |
| 394 * default source IP. It's a shame so much effort happens | |
| 395 * twice. Oh well. | |
| 396 */ | |
| 397 fwd_rewrite_src++; | |
| 398 #endif /* IPFIREWALL_FORWARD */ | |
| 399 } | |
| 400 #endif /* notdef */ | |
| 401 #ifdef ALTQ | |
| 402 /* | |
| 403 * disable packet drop hack. | |
| 404 * packetdrop should be done by queueing. | |
| 405 */ | |
| 406 #else /* !ALTQ */ | |
| 407 /* | |
| 408 * Verify that we have any chance at all of being able to queue | |
| 409 * the packet or packet fragments | |
| 410 */ | |
| 411 if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >= | |
| 412 ifp->if_snd.ifq_maxlen) { | |
| 413 error = ENOBUFS; | |
| 414 goto bad; | |
| 415 } | |
| 416 #endif /* !ALTQ */ | |
| 417 | |
| 418 /* | |
| 419 * Look for broadcast address and | |
| 420 * and verify user is allowed to send | |
| 421 * such a packet. | |
| 422 */ | |
| 423 if (isbroadcast) { | |
| 424 if ((ifp->if_flags & IFF_BROADCAST) == 0) { | |
| 425 error = EADDRNOTAVAIL; | |
| 426 goto bad; | |
| 427 } | |
| 428 if ((flags & IP_ALLOWBROADCAST) == 0) { | |
| 429 error = EACCES; | |
| 430 goto bad; | |
| 431 } | |
| 432 /* don't allow broadcast messages to be fragmented */ | |
| 433 if ((u_short)ip->ip_len > ifp->if_mtu) { | |
| 434 error = EMSGSIZE; | |
| 435 goto bad; | |
| 436 } | |
| 437 m->m_flags |= M_BCAST; | |
| 438 } else { | |
| 439 m->m_flags &= ~M_BCAST; | |
| 440 } | |
| 441 | |
| 442 sendit: | |
| 443 #ifdef IPSEC | |
| 444 /* get SP for this packet */ | |
| 445 if (so == NULL) | |
| 446 sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, flags, &error); | |
| 447 else | |
| 448 sp = ipsec4_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); | |
| 449 | |
| 450 if (sp == NULL) { | |
| 451 ipsecstat.out_inval++; | |
| 452 goto bad; | |
| 453 } | |
| 454 | |
| 455 error = 0; | |
| 456 | |
| 457 /* check policy */ | |
| 458 switch (sp->policy) { | |
| 459 case IPSEC_POLICY_DISCARD: | |
| 460 /* | |
| 461 * This packet is just discarded. | |
| 462 */ | |
| 463 ipsecstat.out_polvio++; | |
| 464 goto bad; | |
| 465 | |
| 466 case IPSEC_POLICY_BYPASS: | |
| 467 case IPSEC_POLICY_NONE: | |
| 468 /* no need to do IPsec. */ | |
| 469 goto skip_ipsec; | |
| 470 | |
| 471 case IPSEC_POLICY_IPSEC: | |
| 472 if (sp->req == NULL) { | |
| 473 /* acquire a policy */ | |
| 474 error = key_spdacquire(sp); | |
| 475 goto bad; | |
| 476 } | |
| 477 break; | |
| 478 | |
| 479 case IPSEC_POLICY_ENTRUST: | |
| 480 default: | |
| 481 printf("ip_output: Invalid policy found. %d\n", sp->policy); | |
| 482 } | |
| 483 { | |
| 484 struct ipsec_output_state state; | |
| 485 bzero(&state, sizeof(state)); | |
| 486 state.m = m; | |
| 487 if (flags & IP_ROUTETOIF) { | |
| 488 state.ro = &iproute; | |
| 489 bzero(&iproute, sizeof(iproute)); | |
| 490 } else | |
| 491 state.ro = ro; | |
| 492 state.dst = (struct sockaddr *)dst; | |
| 493 | |
| 494 ip->ip_sum = 0; | |
| 495 | |
| 496 /* | |
| 497 * XXX | |
| 498 * delayed checksums are not currently compatible with IPsec | |
| 499 */ | |
| 500 if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { | |
| 501 in_delayed_cksum(m); | |
| 502 m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; | |
| 503 } | |
| 504 | |
| 505 HTONS(ip->ip_len); | |
| 506 HTONS(ip->ip_off); | |
| 507 | |
| 508 error = ipsec4_output(&state, sp, flags); | |
| 509 | |
| 510 m = state.m; | |
| 511 if (flags & IP_ROUTETOIF) { | |
| 512 /* | |
| 513 * if we have tunnel mode SA, we may need to ignore | |
| 514 * IP_ROUTETOIF. | |
| 515 */ | |
| 516 if (state.ro != &iproute || state.ro->ro_rt != NULL) { | |
| 517 flags &= ~IP_ROUTETOIF; | |
| 518 ro = state.ro; | |
| 519 } | |
| 520 } else | |
| 521 ro = state.ro; | |
| 522 dst = (struct sockaddr_in *)state.dst; | |
| 523 if (error) { | |
| 524 /* mbuf is already reclaimed in ipsec4_output. */ | |
| 525 m0 = NULL; | |
| 526 switch (error) { | |
| 527 case EHOSTUNREACH: | |
| 528 case ENETUNREACH: | |
| 529 case EMSGSIZE: | |
| 530 case ENOBUFS: | |
| 531 case ENOMEM: | |
| 532 break; | |
| 533 default: | |
| 534 printf("ip4_output (ipsec): error code %d\n", error); | |
| 535 /*fall through*/ | |
| 536 case ENOENT: | |
| 537 /* don't show these error codes to the user */ | |
| 538 error = 0; | |
| 539 break; | |
| 540 } | |
| 541 goto bad; | |
| 542 } | |
| 543 | |
| 544 /* be sure to update variables that are affected by ipsec4_output() */ | |
| 545 ip = mtod(m, struct ip *); | |
| 546 #ifdef _IP_VHL | |
| 547 hlen = IP_VHL_HL(ip->ip_vhl) << 2; | |
| 548 #else | |
| 549 hlen = ip->ip_hl << 2; | |
| 550 #endif | |
| 551 if (ro->ro_rt == NULL) { | |
| 552 if ((flags & IP_ROUTETOIF) == 0) { | |
| 553 printf("ip_output: " | |
| 554 "can't update route after IPsec processing\n"); | |
| 555 error = EHOSTUNREACH; /*XXX*/ | |
| 556 goto bad; | |
| 557 } | |
| 558 } else { | |
| 559 if (state.encap) { | |
| 560 ia = ifatoia(ro->ro_rt->rt_ifa); | |
| 561 ifp = ro->ro_rt->rt_ifp; | |
| 562 } | |
| 563 } | |
| 564 } | |
| 565 | |
| 566 /* make it flipped, again. */ | |
| 567 NTOHS(ip->ip_len); | |
| 568 NTOHS(ip->ip_off); | |
| 569 skip_ipsec: | |
| 570 #endif /*IPSEC*/ | |
| 571 | |
| 572 /* | |
| 573 * IpHack's section. | |
| 574 * - Xlate: translate packet's addr/port (NAT). | |
| 575 * - Firewall: deny/allow/etc. | |
| 576 * - Wrap: fake packet's addr/port <unimpl.> | |
| 577 * - Encapsulate: put it in another IP and send out. <unimp.> | |
| 578 */ | |
| 579 if (fr_checkp) { | |
| 580 struct mbuf *m1 = m; | |
| 581 | |
| 582 if ((error = (*fr_checkp)(ip, hlen, ifp, 1, &m1)) || !m1) | |
| 583 goto done; | |
| 584 ip = mtod(m = m1, struct ip *); | |
| 585 } | |
| 586 | |
| 587 /* | |
| 588 * Check with the firewall... | |
| 589 */ | |
| 590 if (fw_enable && ip_fw_chk_ptr) { | |
| 591 struct sockaddr_in *old = dst; | |
| 592 | |
| 593 off = (*ip_fw_chk_ptr)(&ip, | |
| 594 hlen, ifp, &divert_cookie, &m, &rule, &dst); | |
| 595 /* | |
| 596 * On return we must do the following: | |
| 597 * IP_FW_PORT_DENY_FLAG -> drop the pkt (XXX new) | |
| 598 * 1<=off<= 0xffff -> DIVERT | |
| 599 * (off & IP_FW_PORT_DYNT_FLAG) -> send to a DUMMYNET pipe | |
| 600 * (off & IP_FW_PORT_TEE_FLAG) -> TEE the packet | |
| 601 * dst != old -> IPFIREWALL_FORWARD | |
| 602 * off==0, dst==old -> accept | |
| 603 * If some of the above modules is not compiled in, then | |
| 604 * we should't have to check the corresponding condition | |
| 605 * (because the ipfw control socket should not accept | |
| 606 * unsupported rules), but better play safe and drop | |
| 607 * packets in case of doubt. | |
| 608 */ | |
| 609 if ( (off & IP_FW_PORT_DENY_FLAG) || m == NULL) { | |
| 610 if (m) | |
| 611 m_freem(m); | |
| 612 error = EACCES ; | |
| 613 goto done ; | |
| 614 } | |
| 615 ip = mtod(m, struct ip *); | |
| 616 if (off == 0 && dst == old) /* common case */ | |
| 617 goto pass ; | |
| 618 #ifdef DUMMYNET | |
| 619 if ((off & IP_FW_PORT_DYNT_FLAG) != 0) { | |
| 620 /* | |
| 621 * pass the pkt to dummynet. Need to include | |
| 622 * pipe number, m, ifp, ro, dst because these are | |
| 623 * not recomputed in the next pass. | |
| 624 * All other parameters have been already used and | |
| 625 * so they are not needed anymore. | |
| 626 * XXX note: if the ifp or ro entry are deleted | |
| 627 * while a pkt is in dummynet, we are in trouble! | |
| 628 */ | |
| 629 error = dummynet_io(off & 0xffff, DN_TO_IP_OUT, m, | |
| 630 ifp,ro,dst,rule, flags); | |
| 631 goto done; | |
| 632 } | |
| 633 #endif | |
| 634 #ifdef IPDIVERT | |
| 635 if (off != 0 && (off & IP_FW_PORT_DYNT_FLAG) == 0) { | |
| 636 struct mbuf *clone = NULL; | |
| 637 | |
| 638 /* Clone packet if we're doing a 'tee' */ | |
| 639 if ((off & IP_FW_PORT_TEE_FLAG) != 0) | |
| 640 clone = m_dup(m, M_DONTWAIT); | |
| 641 | |
| 642 /* | |
| 643 * XXX | |
| 644 * delayed checksums are not currently compatible | |
| 645 * with divert sockets. | |
| 646 */ | |
| 647 if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { | |
| 648 in_delayed_cksum(m); | |
| 649 m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; | |
| 650 } | |
| 651 | |
| 652 /* Restore packet header fields to original values */ | |
| 653 HTONS(ip->ip_len); | |
| 654 HTONS(ip->ip_off); | |
| 655 | |
| 656 /* Deliver packet to divert input routine */ | |
| 657 ip_divert_cookie = divert_cookie; | |
| 658 divert_packet(m, 0, off & 0xffff); | |
| 659 | |
| 660 /* If 'tee', continue with original packet */ | |
| 661 if (clone != NULL) { | |
| 662 m = clone; | |
| 663 ip = mtod(m, struct ip *); | |
| 664 goto pass; | |
| 665 } | |
| 666 goto done; | |
| 667 } | |
| 668 #endif | |
| 669 | |
| 670 #ifdef IPFIREWALL_FORWARD | |
| 671 /* Here we check dst to make sure it's directly reachable on the | |
| 672 * interface we previously thought it was. | |
| 673 * If it isn't (which may be likely in some situations) we have | |
| 674 * to re-route it (ie, find a route for the next-hop and the | |
| 675 * associated interface) and set them here. This is nested | |
| 676 * forwarding which in most cases is undesirable, except where | |
| 677 * such control is nigh impossible. So we do it here. | |
| 678 * And I'm babbling. | |
| 679 */ | |
| 680 if (off == 0 && old != dst) { | |
| 681 struct in_ifaddr *ia; | |
| 682 | |
| 683 /* It's changed... */ | |
| 684 /* There must be a better way to do this next line... */ | |
| 685 static struct route sro_fwd, *ro_fwd = &sro_fwd; | |
| 686 #ifdef IPFIREWALL_FORWARD_DEBUG | |
| 687 printf("IPFIREWALL_FORWARD: New dst ip: "); | |
| 688 print_ip(dst->sin_addr); | |
| 689 printf("\n"); | |
| 690 #endif | |
| 691 /* | |
| 692 * We need to figure out if we have been forwarded | |
| 693 * to a local socket. If so then we should somehow | |
| 694 * "loop back" to ip_input, and get directed to the | |
| 695 * PCB as if we had received this packet. This is | |
| 696 * because it may be dificult to identify the packets | |
| 697 * you want to forward until they are being output | |
| 698 * and have selected an interface. (e.g. locally | |
| 699 * initiated packets) If we used the loopback inteface, | |
| 700 * we would not be able to control what happens | |
| 701 * as the packet runs through ip_input() as | |
| 702 * it is done through a ISR. | |
| 703 */ | |
| 704 TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { | |
| 705 /* | |
| 706 * If the addr to forward to is one | |
| 707 * of ours, we pretend to | |
| 708 * be the destination for this packet. | |
| 709 */ | |
| 710 if (IA_SIN(ia)->sin_addr.s_addr == | |
| 711 dst->sin_addr.s_addr) | |
| 712 break; | |
| 713 } | |
| 714 if (ia) { | |
| 715 /* tell ip_input "dont filter" */ | |
| 716 ip_fw_fwd_addr = dst; | |
| 717 if (m->m_pkthdr.rcvif == NULL) | |
| 718 m->m_pkthdr.rcvif = ifunit("lo0"); | |
| 719 if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { | |
| 720 m->m_pkthdr.csum_flags |= | |
| 721 CSUM_DATA_VALID | CSUM_PSEUDO_HDR; | |
| 722 m0->m_pkthdr.csum_data = 0xffff; | |
| 723 } | |
| 724 m->m_pkthdr.csum_flags |= | |
| 725 CSUM_IP_CHECKED | CSUM_IP_VALID; | |
| 726 HTONS(ip->ip_len); | |
| 727 HTONS(ip->ip_off); | |
| 728 ip_input(m); | |
| 729 goto done; | |
| 730 } | |
| 731 /* Some of the logic for this was | |
| 732 * nicked from above. | |
| 733 * | |
| 734 * This rewrites the cached route in a local PCB. | |
| 735 * Is this what we want to do? | |
| 736 */ | |
| 737 bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst)); | |
| 738 | |
| 739 ro_fwd->ro_rt = 0; | |
| 740 rtalloc_ign(ro_fwd, RTF_PRCLONING); | |
| 741 | |
| 742 if (ro_fwd->ro_rt == 0) { | |
| 743 ipstat.ips_noroute++; | |
| 744 error = EHOSTUNREACH; | |
| 745 goto bad; | |
| 746 } | |
| 747 | |
| 748 ia = ifatoia(ro_fwd->ro_rt->rt_ifa); | |
| 749 ifp = ro_fwd->ro_rt->rt_ifp; | |
| 750 ro_fwd->ro_rt->rt_use++; | |
| 751 if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY) | |
| 752 dst = (struct sockaddr_in *)ro_fwd->ro_rt->rt_gateway; | |
| 753 if (ro_fwd->ro_rt->rt_flags & RTF_HOST) | |
| 754 isbroadcast = | |
| 755 (ro_fwd->ro_rt->rt_flags & RTF_BROADCAST); | |
| 756 else | |
| 757 isbroadcast = in_broadcast(dst->sin_addr, ifp); | |
| 758 RTFREE(ro->ro_rt); | |
| 759 ro->ro_rt = ro_fwd->ro_rt; | |
| 760 dst = (struct sockaddr_in *)&ro_fwd->ro_dst; | |
| 761 | |
| 762 /* | |
| 763 * If we added a default src ip earlier, | |
| 764 * which would have been gotten from the-then | |
| 765 * interface, do it again, from the new one. | |
| 766 */ | |
| 767 if (fwd_rewrite_src) | |
| 768 ip->ip_src = IA_SIN(ia)->sin_addr; | |
| 769 goto pass ; | |
| 770 } | |
| 771 #endif /* IPFIREWALL_FORWARD */ | |
| 772 /* | |
| 773 * if we get here, none of the above matches, and | |
| 774 * we have to drop the pkt | |
| 775 */ | |
| 776 m_freem(m); | |
| 777 error = EACCES; /* not sure this is the right error msg */ | |
| 778 goto done; | |
| 779 } | |
| 780 | |
| 781 pass: | |
| 782 m->m_pkthdr.csum_flags |= CSUM_IP; | |
| 783 sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist; | |
| 784 if (sw_csum & CSUM_DELAY_DATA) { | |
| 785 in_delayed_cksum(m); | |
| 786 sw_csum &= ~CSUM_DELAY_DATA; | |
| 787 } | |
| 788 m->m_pkthdr.csum_flags &= ifp->if_hwassist; | |
| 789 | |
| 790 /* | |
| 791 * If small enough for interface, or the interface will take | |
| 792 * care of the fragmentation for us, can just send directly. | |
| 793 */ | |
| 794 if ((u_short)ip->ip_len <= ifp->if_mtu || | |
| 795 ifp->if_hwassist & CSUM_FRAGMENT) { | |
| 796 HTONS(ip->ip_len); | |
| 797 HTONS(ip->ip_off); | |
| 798 ip->ip_sum = 0; | |
| 799 if (sw_csum & CSUM_DELAY_IP) { | |
| 800 if (ip->ip_vhl == IP_VHL_BORING) { | |
| 801 ip->ip_sum = in_cksum_hdr(ip); | |
| 802 } else { | |
| 803 ip->ip_sum = in_cksum(m, hlen); | |
| 804 } | |
| 805 } | |
| 806 | |
| 807 /* Record statistics for this interface address. */ | |
| 808 if (!(flags & IP_FORWARDING) && ia != NULL) { | |
| 809 ia->ia_ifa.if_opackets++; | |
| 810 ia->ia_ifa.if_obytes += m->m_pkthdr.len; | |
| 811 } | |
| 812 | |
| 813 #ifdef IPSEC | |
| 814 /* clean ipsec history once it goes out of the node */ | |
| 815 ipsec_delaux(m); | |
| 816 #endif | |
| 817 | |
| 818 error = (*ifp->if_output)(ifp, m, | |
| 819 (struct sockaddr *)dst, ro->ro_rt); | |
| 820 goto done; | |
| 821 } | |
| 822 /* | |
| 823 * Too large for interface; fragment if possible. | |
| 824 * Must be able to put at least 8 bytes per fragment. | |
| 825 */ | |
| 826 if (ip->ip_off & IP_DF) { | |
| 827 error = EMSGSIZE; | |
| 828 /* | |
| 829 * This case can happen if the user changed the MTU | |
| 830 * of an interface after enabling IP on it. Because | |
| 831 * most netifs don't keep track of routes pointing to | |
| 832 * them, there is no way for one to update all its | |
| 833 * routes when the MTU is changed. | |
| 834 */ | |
| 835 if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) | |
| 836 && !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU) | |
| 837 && (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) { | |
| 838 ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu; | |
| 839 } | |
| 840 ipstat.ips_cantfrag++; | |
| 841 goto bad; | |
| 842 } | |
| 843 len = (ifp->if_mtu - hlen) &~ 7; | |
| 844 if (len < 8) { | |
| 845 error = EMSGSIZE; | |
| 846 goto bad; | |
| 847 } | |
| 848 | |
| 849 /* | |
| 850 * if the interface will not calculate checksums on | |
| 851 * fragmented packets, then do it here. | |
| 852 */ | |
| 853 if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA && | |
| 854 (ifp->if_hwassist & CSUM_IP_FRAGS) == 0) { | |
| 855 in_delayed_cksum(m); | |
| 856 m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; | |
| 857 } | |
| 858 | |
| 859 { | |
| 860 int mhlen, firstlen = len; | |
| 861 struct mbuf **mnext = &m->m_nextpkt; | |
| 862 int nfrags = 1; | |
| 863 | |
| 864 /* | |
| 865 * Loop through length of segment after first fragment, | |
| 866 * make new header and copy data of each part and link onto chain. | |
| 867 */ | |
| 868 m0 = m; | |
| 869 mhlen = sizeof (struct ip); | |
| 870 for (off = hlen + len; off < (u_short)ip->ip_len; off += len) { | |
| 871 MGETHDR(m, M_DONTWAIT, MT_HEADER); | |
| 872 if (m == 0) { | |
| 873 error = ENOBUFS; | |
| 874 ipstat.ips_odropped++; | |
| 875 goto sendorfree; | |
| 876 } | |
| 877 m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG; | |
| 878 m->m_data += max_linkhdr; | |
| 879 mhip = mtod(m, struct ip *); | |
| 880 *mhip = *ip; | |
| 881 if (hlen > sizeof (struct ip)) { | |
| 882 mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); | |
| 883 mhip->ip_vhl = IP_MAKE_VHL(IPVERSION, mhlen >> 2); | |
| 884 } | |
| 885 m->m_len = mhlen; | |
| 886 mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); | |
| 887 if (ip->ip_off & IP_MF) | |
| 888 mhip->ip_off |= IP_MF; | |
| 889 if (off + len >= (u_short)ip->ip_len) | |
| 890 len = (u_short)ip->ip_len - off; | |
| 891 else | |
| 892 mhip->ip_off |= IP_MF; | |
| 893 mhip->ip_len = htons((u_short)(len + mhlen)); | |
| 894 m->m_next = m_copy(m0, off, len); | |
| 895 if (m->m_next == 0) { | |
| 896 (void) m_free(m); | |
| 897 error = ENOBUFS; /* ??? */ | |
| 898 ipstat.ips_odropped++; | |
| 899 goto sendorfree; | |
| 900 } | |
| 901 m->m_pkthdr.len = mhlen + len; | |
| 902 m->m_pkthdr.rcvif = (struct ifnet *)0; | |
| 903 m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags; | |
| 904 HTONS(mhip->ip_off); | |
| 905 mhip->ip_sum = 0; | |
| 906 if (sw_csum & CSUM_DELAY_IP) { | |
| 907 if (mhip->ip_vhl == IP_VHL_BORING) { | |
| 908 mhip->ip_sum = in_cksum_hdr(mhip); | |
| 909 } else { | |
| 910 mhip->ip_sum = in_cksum(m, mhlen); | |
| 911 } | |
| 912 } | |
| 913 *mnext = m; | |
| 914 mnext = &m->m_nextpkt; | |
| 915 nfrags++; | |
| 916 } | |
| 917 ipstat.ips_ofragments += nfrags; | |
| 918 | |
| 919 /* set first/last markers for fragment chain */ | |
| 920 m->m_flags |= M_LASTFRAG; | |
| 921 m0->m_flags |= M_FIRSTFRAG | M_FRAG; | |
| 922 m0->m_pkthdr.csum_data = nfrags; | |
| 923 | |
| 924 /* | |
| 925 * Update first fragment by trimming what's been copied out | |
| 926 * and updating header, then send each fragment (in order). | |
| 927 */ | |
| 928 m = m0; | |
| 929 m_adj(m, hlen + firstlen - (u_short)ip->ip_len); | |
| 930 m->m_pkthdr.len = hlen + firstlen; | |
| 931 ip->ip_len = htons((u_short)m->m_pkthdr.len); | |
| 932 ip->ip_off |= IP_MF; | |
| 933 HTONS(ip->ip_off); | |
| 934 ip->ip_sum = 0; | |
| 935 if (sw_csum & CSUM_DELAY_IP) { | |
| 936 if (ip->ip_vhl == IP_VHL_BORING) { | |
| 937 ip->ip_sum = in_cksum_hdr(ip); | |
| 938 } else { | |
| 939 ip->ip_sum = in_cksum(m, hlen); | |
| 940 } | |
| 941 } | |
| 942 sendorfree: | |
| 943 for (m = m0; m; m = m0) { | |
| 944 m0 = m->m_nextpkt; | |
| 945 m->m_nextpkt = 0; | |
| 946 #ifdef IPSEC | |
| 947 /* clean ipsec history once it goes out of the node */ | |
| 948 ipsec_delaux(m); | |
| 949 #endif | |
| 950 if (error == 0) { | |
| 951 /* Record statistics for this interface address. */ | |
| 952 if (ia != NULL) { | |
| 953 ia->ia_ifa.if_opackets++; | |
| 954 ia->ia_ifa.if_obytes += m->m_pkthdr.len; | |
| 955 } | |
| 956 | |
| 957 error = (*ifp->if_output)(ifp, m, | |
| 958 (struct sockaddr *)dst, ro->ro_rt); | |
| 959 } else | |
| 960 m_freem(m); | |
| 961 } | |
| 962 | |
| 963 if (error == 0) | |
| 964 ipstat.ips_fragmented++; | |
| 965 } | |
| 966 done: | |
| 967 #ifdef IPSEC | |
| 968 if (ro == &iproute && ro->ro_rt) { | |
| 969 RTFREE(ro->ro_rt); | |
| 970 ro->ro_rt = NULL; | |
| 971 } | |
| 972 if (sp != NULL) { | |
| 973 KEYDEBUG(KEYDEBUG_IPSEC_STAMP, | |
| 974 printf("DP ip_output call free SP:%p\n", sp)); | |
| 975 key_freesp(sp); | |
| 976 } | |
| 977 #endif /* IPSEC */ | |
| 978 return (error); | |
| 979 bad: | |
| 980 m_freem(m0); | |
| 981 goto done; | |
| 982 } | |
| 983 | |
| 984 void | |
| 985 in_delayed_cksum(struct mbuf *m) | |
| 986 { | |
| 987 struct ip *ip; | |
| 988 u_short csum, offset; | |
| 989 | |
| 990 ip = mtod(m, struct ip *); | |
| 991 offset = IP_VHL_HL(ip->ip_vhl) << 2 ; | |
| 992 csum = in_cksum_skip(m, ip->ip_len, offset); | |
| 993 if (m->m_pkthdr.csum_flags & CSUM_UDP && csum == 0) | |
| 994 csum = 0xffff; | |
| 995 offset += m->m_pkthdr.csum_data; /* checksum offset */ | |
| 996 | |
| 997 if (offset + sizeof(u_short) > m->m_len) { | |
| 998 printf("delayed m_pullup, m->len: %d off: %d p: %d\n", | |
| 999 m->m_len, offset, ip->ip_p); | |
| 1000 /* | |
| 1001 * XXX | |
| 1002 * this shouldn't happen, but if it does, the | |
| 1003 * correct behavior may be to insert the checksum | |
| 1004 * in the existing chain instead of rearranging it. | |
| 1005 */ | |
| 1006 m = m_pullup(m, offset + sizeof(u_short)); | |
| 1007 } | |
| 1008 *(u_short *)(m->m_data + offset) = csum; | |
| 1009 } | |
| 1010 | |
| 1011 /* | |
| 1012 * Insert IP options into preformed packet. | |
| 1013 * Adjust IP destination as required for IP source routing, | |
| 1014 * as indicated by a non-zero in_addr at the start of the options. | |
| 1015 * | |
| 1016 * XXX This routine assumes that the packet has no options in place. | |
| 1017 */ | |
| 1018 static struct mbuf * | |
| 1019 ip_insertoptions(m, opt, phlen) | |
| 1020 register struct mbuf *m; | |
| 1021 struct mbuf *opt; | |
| 1022 int *phlen; | |
| 1023 { | |
| 1024 register struct ipoption *p = mtod(opt, struct ipoption *); | |
| 1025 struct mbuf *n; | |
| 1026 register struct ip *ip = mtod(m, struct ip *); | |
| 1027 unsigned optlen; | |
| 1028 | |
| 1029 optlen = opt->m_len - sizeof(p->ipopt_dst); | |
| 1030 if (optlen + (u_short)ip->ip_len > IP_MAXPACKET) | |
| 1031 return (m); /* XXX should fail */ | |
| 1032 if (p->ipopt_dst.s_addr) | |
| 1033 ip->ip_dst = p->ipopt_dst; | |
| 1034 if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) { | |
| 1035 MGETHDR(n, M_DONTWAIT, MT_HEADER); | |
| 1036 if (n == 0) | |
| 1037 return (m); | |
| 1038 n->m_pkthdr.rcvif = (struct ifnet *)0; | |
| 1039 n->m_pkthdr.len = m->m_pkthdr.len + optlen; | |
| 1040 m->m_len -= sizeof(struct ip); | |
| 1041 m->m_data += sizeof(struct ip); | |
| 1042 n->m_next = m; | |
| 1043 m = n; | |
| 1044 m->m_len = optlen + sizeof(struct ip); | |
| 1045 m->m_data += max_linkhdr; | |
| 1046 (void)memcpy(mtod(m, void *), ip, sizeof(struct ip)); | |
| 1047 } else { | |
| 1048 m->m_data -= optlen; | |
| 1049 m->m_len += optlen; | |
| 1050 m->m_pkthdr.len += optlen; | |
| 1051 ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); | |
| 1052 } | |
| 1053 ip = mtod(m, struct ip *); | |
| 1054 bcopy(p->ipopt_list, ip + 1, optlen); | |
| 1055 *phlen = sizeof(struct ip) + optlen; | |
| 1056 ip->ip_vhl = IP_MAKE_VHL(IPVERSION, *phlen >> 2); | |
| 1057 ip->ip_len += optlen; | |
| 1058 return (m); | |
| 1059 } | |
| 1060 | |
| 1061 /* | |
| 1062 * Copy options from ip to jp, | |
| 1063 * omitting those not copied during fragmentation. | |
| 1064 */ | |
| 1065 int | |
| 1066 ip_optcopy(ip, jp) | |
| 1067 struct ip *ip, *jp; | |
| 1068 { | |
| 1069 register u_char *cp, *dp; | |
| 1070 int opt, optlen, cnt; | |
| 1071 | |
| 1072 cp = (u_char *)(ip + 1); | |
| 1073 dp = (u_char *)(jp + 1); | |
| 1074 cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip); | |
| 1075 for (; cnt > 0; cnt -= optlen, cp += optlen) { | |
| 1076 opt = cp[0]; | |
| 1077 if (opt == IPOPT_EOL) | |
| 1078 break; | |
| 1079 if (opt == IPOPT_NOP) { | |
| 1080 /* Preserve for IP mcast tunnel's LSRR alignment. */ | |
| 1081 *dp++ = IPOPT_NOP; | |
| 1082 optlen = 1; | |
| 1083 continue; | |
| 1084 } | |
| 1085 #ifdef DIAGNOSTIC | |
| 1086 if (cnt < IPOPT_OLEN + sizeof(*cp)) | |
| 1087 panic("malformed IPv4 option passed to ip_optcopy"); | |
| 1088 #endif | |
| 1089 optlen = cp[IPOPT_OLEN]; | |
| 1090 #ifdef DIAGNOSTIC | |
| 1091 if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) | |
| 1092 panic("malformed IPv4 option passed to ip_optcopy"); | |
| 1093 #endif | |
| 1094 /* bogus lengths should have been caught by ip_dooptions */ | |
| 1095 if (optlen > cnt) | |
| 1096 optlen = cnt; | |
| 1097 if (IPOPT_COPIED(opt)) { | |
| 1098 bcopy(cp, dp, optlen); | |
| 1099 dp += optlen; | |
| 1100 } | |
| 1101 } | |
| 1102 for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) | |
| 1103 *dp++ = IPOPT_EOL; | |
| 1104 return (optlen); | |
| 1105 } | |
| 1106 | |
| 1107 /* | |
| 1108 * IP socket option processing. | |
| 1109 */ | |
| 1110 int | |
| 1111 ip_ctloutput(so, sopt) | |
| 1112 struct socket *so; | |
| 1113 struct sockopt *sopt; | |
| 1114 { | |
| 1115 struct inpcb *inp = sotoinpcb(so); | |
| 1116 int error, optval; | |
| 1117 | |
| 1118 error = optval = 0; | |
| 1119 if (sopt->sopt_level != IPPROTO_IP) { | |
| 1120 return (EINVAL); | |
| 1121 } | |
| 1122 | |
| 1123 switch (sopt->sopt_dir) { | |
| 1124 case SOPT_SET: | |
| 1125 switch (sopt->sopt_name) { | |
| 1126 case IP_OPTIONS: | |
| 1127 #ifdef notyet | |
| 1128 case IP_RETOPTS: | |
| 1129 #endif | |
| 1130 { | |
| 1131 struct mbuf *m; | |
| 1132 if (sopt->sopt_valsize > MLEN) { | |
| 1133 error = EMSGSIZE; | |
| 1134 break; | |
| 1135 } | |
| 1136 MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER); | |
| 1137 if (m == 0) { | |
| 1138 error = ENOBUFS; | |
| 1139 break; | |
| 1140 } | |
| 1141 m->m_len = sopt->sopt_valsize; | |
| 1142 error = sooptcopyin(sopt, mtod(m, char *), m->m_len, | |
| 1143 m->m_len); | |
| 1144 | |
| 1145 return (ip_pcbopts(sopt->sopt_name, &inp->inp_options, | |
| 1146 m)); | |
| 1147 } | |
| 1148 | |
| 1149 case IP_TOS: | |
| 1150 case IP_TTL: | |
| 1151 case IP_RECVOPTS: | |
| 1152 case IP_RECVRETOPTS: | |
| 1153 case IP_RECVDSTADDR: | |
| 1154 case IP_RECVIF: | |
| 1155 #if defined(NFAITH) && NFAITH > 0 | |
| 1156 case IP_FAITH: | |
| 1157 #endif | |
| 1158 error = sooptcopyin(sopt, &optval, sizeof optval, | |
| 1159 sizeof optval); | |
| 1160 if (error) | |
| 1161 break; | |
| 1162 | |
| 1163 switch (sopt->sopt_name) { | |
| 1164 case IP_TOS: | |
| 1165 inp->inp_ip_tos = optval; | |
| 1166 break; | |
| 1167 | |
| 1168 case IP_TTL: | |
| 1169 inp->inp_ip_ttl = optval; | |
| 1170 break; | |
| 1171 #define OPTSET(bit) \ | |
| 1172 if (optval) \ | |
| 1173 inp->inp_flags |= bit; \ | |
| 1174 else \ | |
| 1175 inp->inp_flags &= ~bit; | |
| 1176 | |
| 1177 case IP_RECVOPTS: | |
| 1178 OPTSET(INP_RECVOPTS); | |
| 1179 break; | |
| 1180 | |
| 1181 case IP_RECVRETOPTS: | |
| 1182 OPTSET(INP_RECVRETOPTS); | |
| 1183 break; | |
| 1184 | |
| 1185 case IP_RECVDSTADDR: | |
| 1186 OPTSET(INP_RECVDSTADDR); | |
| 1187 break; | |
| 1188 | |
| 1189 case IP_RECVIF: | |
| 1190 OPTSET(INP_RECVIF); | |
| 1191 break; | |
| 1192 | |
| 1193 #if defined(NFAITH) && NFAITH > 0 | |
| 1194 case IP_FAITH: | |
| 1195 OPTSET(INP_FAITH); | |
| 1196 break; | |
| 1197 #endif | |
| 1198 } | |
| 1199 break; | |
| 1200 #undef OPTSET | |
| 1201 | |
| 1202 case IP_MULTICAST_IF: | |
| 1203 case IP_MULTICAST_VIF: | |
| 1204 case IP_MULTICAST_TTL: | |
| 1205 case IP_MULTICAST_LOOP: | |
| 1206 case IP_ADD_MEMBERSHIP: | |
| 1207 case IP_DROP_MEMBERSHIP: | |
| 1208 error = ip_setmoptions(sopt, &inp->inp_moptions); | |
| 1209 break; | |
| 1210 | |
| 1211 case IP_PORTRANGE: | |
| 1212 error = sooptcopyin(sopt, &optval, sizeof optval, | |
| 1213 sizeof optval); | |
| 1214 if (error) | |
| 1215 break; | |
| 1216 | |
| 1217 switch (optval) { | |
| 1218 case IP_PORTRANGE_DEFAULT: | |
| 1219 inp->inp_flags &= ~(INP_LOWPORT); | |
| 1220 inp->inp_flags &= ~(INP_HIGHPORT); | |
| 1221 break; | |
| 1222 | |
| 1223 case IP_PORTRANGE_HIGH: | |
| 1224 inp->inp_flags &= ~(INP_LOWPORT); | |
| 1225 inp->inp_flags |= INP_HIGHPORT; | |
| 1226 break; | |
| 1227 | |
| 1228 case IP_PORTRANGE_LOW: | |
| 1229 inp->inp_flags &= ~(INP_HIGHPORT); | |
| 1230 inp->inp_flags |= INP_LOWPORT; | |
| 1231 break; | |
| 1232 | |
| 1233 default: | |
| 1234 error = EINVAL; | |
| 1235 break; | |
| 1236 } | |
| 1237 break; | |
| 1238 | |
| 1239 #ifdef IPSEC | |
| 1240 case IP_IPSEC_POLICY: | |
| 1241 { | |
| 1242 caddr_t req; | |
| 1243 size_t len = 0; | |
| 1244 int priv; | |
| 1245 struct mbuf *m; | |
| 1246 int optname; | |
| 1247 | |
| 1248 if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ | |
| 1249 break; | |
| 1250 if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ | |
| 1251 break; | |
| 1252 priv = (sopt->sopt_p != NULL && | |
| 1253 suser(sopt->sopt_p) != 0) ? 0 : 1; | |
| 1254 req = mtod(m, caddr_t); | |
| 1255 len = m->m_len; | |
| 1256 optname = sopt->sopt_name; | |
| 1257 error = ipsec4_set_policy(inp, optname, req, len, priv); | |
| 1258 m_freem(m); | |
| 1259 break; | |
| 1260 } | |
| 1261 #endif /*IPSEC*/ | |
| 1262 | |
| 1263 default: | |
| 1264 error = ENOPROTOOPT; | |
| 1265 break; | |
| 1266 } | |
| 1267 break; | |
| 1268 | |
| 1269 case SOPT_GET: | |
| 1270 switch (sopt->sopt_name) { | |
| 1271 case IP_OPTIONS: | |
| 1272 case IP_RETOPTS: | |
| 1273 if (inp->inp_options) | |
| 1274 error = sooptcopyout(sopt, | |
| 1275 mtod(inp->inp_options, | |
| 1276 char *), | |
| 1277 inp->inp_options->m_len); | |
| 1278 else | |
| 1279 sopt->sopt_valsize = 0; | |
| 1280 break; | |
| 1281 | |
| 1282 case IP_TOS: | |
| 1283 case IP_TTL: | |
| 1284 case IP_RECVOPTS: | |
| 1285 case IP_RECVRETOPTS: | |
| 1286 case IP_RECVDSTADDR: | |
| 1287 case IP_RECVIF: | |
| 1288 case IP_PORTRANGE: | |
| 1289 #if defined(NFAITH) && NFAITH > 0 | |
| 1290 case IP_FAITH: | |
| 1291 #endif | |
| 1292 switch (sopt->sopt_name) { | |
| 1293 | |
| 1294 case IP_TOS: | |
| 1295 optval = inp->inp_ip_tos; | |
| 1296 break; | |
| 1297 | |
| 1298 case IP_TTL: | |
| 1299 optval = inp->inp_ip_ttl; | |
| 1300 break; | |
| 1301 | |
| 1302 #define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0) | |
| 1303 | |
| 1304 case IP_RECVOPTS: | |
| 1305 optval = OPTBIT(INP_RECVOPTS); | |
| 1306 break; | |
| 1307 | |
| 1308 case IP_RECVRETOPTS: | |
| 1309 optval = OPTBIT(INP_RECVRETOPTS); | |
| 1310 break; | |
| 1311 | |
| 1312 case IP_RECVDSTADDR: | |
| 1313 optval = OPTBIT(INP_RECVDSTADDR); | |
| 1314 break; | |
| 1315 | |
| 1316 case IP_RECVIF: | |
| 1317 optval = OPTBIT(INP_RECVIF); | |
| 1318 break; | |
| 1319 | |
| 1320 case IP_PORTRANGE: | |
| 1321 if (inp->inp_flags & INP_HIGHPORT) | |
| 1322 optval = IP_PORTRANGE_HIGH; | |
| 1323 else if (inp->inp_flags & INP_LOWPORT) | |
| 1324 optval = IP_PORTRANGE_LOW; | |
| 1325 else | |
| 1326 optval = 0; | |
| 1327 break; | |
| 1328 | |
| 1329 #if defined(NFAITH) && NFAITH > 0 | |
| 1330 case IP_FAITH: | |
| 1331 optval = OPTBIT(INP_FAITH); | |
| 1332 break; | |
| 1333 #endif | |
| 1334 } | |
| 1335 error = sooptcopyout(sopt, &optval, sizeof optval); | |
| 1336 break; | |
| 1337 | |
| 1338 case IP_MULTICAST_IF: | |
| 1339 case IP_MULTICAST_VIF: | |
| 1340 case IP_MULTICAST_TTL: | |
| 1341 case IP_MULTICAST_LOOP: | |
| 1342 case IP_ADD_MEMBERSHIP: | |
| 1343 case IP_DROP_MEMBERSHIP: | |
| 1344 error = ip_getmoptions(sopt, inp->inp_moptions); | |
| 1345 break; | |
| 1346 | |
| 1347 #ifdef IPSEC | |
| 1348 case IP_IPSEC_POLICY: | |
| 1349 { | |
| 1350 struct mbuf *m = NULL; | |
| 1351 caddr_t req = NULL; | |
| 1352 size_t len = 0; | |
| 1353 | |
| 1354 if (m != 0) { | |
| 1355 req = mtod(m, caddr_t); | |
| 1356 len = m->m_len; | |
| 1357 } | |
| 1358 error = ipsec4_get_policy(sotoinpcb(so), req, len, &m); | |
| 1359 if (error == 0) | |
| 1360 error = soopt_mcopyout(sopt, m); /* XXX */ | |
| 1361 if (error == 0) | |
| 1362 m_freem(m); | |
| 1363 break; | |
| 1364 } | |
| 1365 #endif /*IPSEC*/ | |
| 1366 | |
| 1367 default: | |
| 1368 error = ENOPROTOOPT; | |
| 1369 break; | |
| 1370 } | |
| 1371 break; | |
| 1372 } | |
| 1373 return (error); | |
| 1374 } | |
| 1375 | |
| 1376 /* | |
| 1377 * Set up IP options in pcb for insertion in output packets. | |
| 1378 * Store in mbuf with pointer in pcbopt, adding pseudo-option | |
| 1379 * with destination address if source routed. | |
| 1380 */ | |
| 1381 static int | |
| 1382 ip_pcbopts(optname, pcbopt, m) | |
| 1383 int optname; | |
| 1384 struct mbuf **pcbopt; | |
| 1385 register struct mbuf *m; | |
| 1386 { | |
| 1387 register int cnt, optlen; | |
| 1388 register u_char *cp; | |
| 1389 u_char opt; | |
| 1390 | |
| 1391 /* turn off any old options */ | |
| 1392 if (*pcbopt) | |
| 1393 (void)m_free(*pcbopt); | |
| 1394 *pcbopt = 0; | |
| 1395 if (m == (struct mbuf *)0 || m->m_len == 0) { | |
| 1396 /* | |
| 1397 * Only turning off any previous options. | |
| 1398 */ | |
| 1399 if (m) | |
| 1400 (void)m_free(m); | |
| 1401 return (0); | |
| 1402 } | |
| 1403 | |
| 1404 #ifndef vax | |
| 1405 if (m->m_len % sizeof(int32_t)) | |
| 1406 goto bad; | |
| 1407 #endif | |
| 1408 /* | |
| 1409 * IP first-hop destination address will be stored before | |
| 1410 * actual options; move other options back | |
| 1411 * and clear it when none present. | |
| 1412 */ | |
| 1413 if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN]) | |
| 1414 goto bad; | |
| 1415 cnt = m->m_len; | |
| 1416 m->m_len += sizeof(struct in_addr); | |
| 1417 cp = mtod(m, u_char *) + sizeof(struct in_addr); | |
| 1418 ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt); | |
| 1419 bzero(mtod(m, caddr_t), sizeof(struct in_addr)); | |
| 1420 | |
| 1421 for (; cnt > 0; cnt -= optlen, cp += optlen) { | |
| 1422 opt = cp[IPOPT_OPTVAL]; | |
| 1423 if (opt == IPOPT_EOL) | |
| 1424 break; | |
| 1425 if (opt == IPOPT_NOP) | |
| 1426 optlen = 1; | |
| 1427 else { | |
| 1428 if (cnt < IPOPT_OLEN + sizeof(*cp)) | |
| 1429 goto bad; | |
| 1430 optlen = cp[IPOPT_OLEN]; | |
| 1431 if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) | |
| 1432 goto bad; | |
| 1433 } | |
| 1434 switch (opt) { | |
| 1435 | |
| 1436 default: | |
| 1437 break; | |
| 1438 | |
| 1439 case IPOPT_LSRR: | |
| 1440 case IPOPT_SSRR: | |
| 1441 /* | |
| 1442 * user process specifies route as: | |
| 1443 * ->A->B->C->D | |
| 1444 * D must be our final destination (but we can't | |
| 1445 * check that since we may not have connected yet). | |
| 1446 * A is first hop destination, which doesn't appear in | |
| 1447 * actual IP option, but is stored before the options. | |
| 1448 */ | |
| 1449 if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) | |
| 1450 goto bad; | |
| 1451 m->m_len -= sizeof(struct in_addr); | |
| 1452 cnt -= sizeof(struct in_addr); | |
| 1453 optlen -= sizeof(struct in_addr); | |
| 1454 cp[IPOPT_OLEN] = optlen; | |
| 1455 /* | |
| 1456 * Move first hop before start of options. | |
| 1457 */ | |
| 1458 bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), | |
| 1459 sizeof(struct in_addr)); | |
| 1460 /* | |
| 1461 * Then copy rest of options back | |
| 1462 * to close up the deleted entry. | |
| 1463 */ | |
| 1464 ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] + | |
| 1465 sizeof(struct in_addr)), | |
| 1466 (caddr_t)&cp[IPOPT_OFFSET+1], | |
| 1467 (unsigned)cnt + sizeof(struct in_addr)); | |
| 1468 break; | |
| 1469 } | |
| 1470 } | |
| 1471 if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr)) | |
| 1472 goto bad; | |
| 1473 *pcbopt = m; | |
| 1474 return (0); | |
| 1475 | |
| 1476 bad: | |
| 1477 (void)m_free(m); | |
| 1478 return (EINVAL); | |
| 1479 } | |
| 1480 | |
| 1481 /* | |
| 1482 * XXX | |
| 1483 * The whole multicast option thing needs to be re-thought. | |
| 1484 * Several of these options are equally applicable to non-multicast | |
| 1485 * transmission, and one (IP_MULTICAST_TTL) totally duplicates a | |
| 1486 * standard option (IP_TTL). | |
| 1487 */ | |
| 1488 | |
| 1489 /* | |
| 1490 * following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index. | |
| 1491 */ | |
| 1492 static struct ifnet * | |
| 1493 ip_multicast_if(a, ifindexp) | |
| 1494 struct in_addr *a; | |
| 1495 int *ifindexp; | |
| 1496 { | |
| 1497 int ifindex; | |
| 1498 struct ifnet *ifp; | |
| 1499 | |
| 1500 if (ifindexp) | |
| 1501 *ifindexp = 0; | |
| 1502 if (ntohl(a->s_addr) >> 24 == 0) { | |
| 1503 ifindex = ntohl(a->s_addr) & 0xffffff; | |
| 1504 if (ifindex < 0 || if_index < ifindex) | |
| 1505 return NULL; | |
| 1506 ifp = ifindex2ifnet[ifindex]; | |
| 1507 if (ifindexp) | |
| 1508 *ifindexp = ifindex; | |
| 1509 } else { | |
| 1510 INADDR_TO_IFP(*a, ifp); | |
| 1511 } | |
| 1512 return ifp; | |
| 1513 } | |
| 1514 | |
| 1515 /* | |
| 1516 * Set the IP multicast options in response to user setsockopt(). | |
| 1517 */ | |
| 1518 static int | |
| 1519 ip_setmoptions(sopt, imop) | |
| 1520 struct sockopt *sopt; | |
| 1521 struct ip_moptions **imop; | |
| 1522 { | |
| 1523 int error = 0; | |
| 1524 int i; | |
| 1525 struct in_addr addr; | |
| 1526 struct ip_mreq mreq; | |
| 1527 struct ifnet *ifp; | |
| 1528 struct ip_moptions *imo = *imop; | |
| 1529 struct route ro; | |
| 1530 struct sockaddr_in *dst; | |
| 1531 int ifindex; | |
| 1532 int s; | |
| 1533 | |
| 1534 if (imo == NULL) { | |
| 1535 /* | |
| 1536 * No multicast option buffer attached to the pcb; | |
| 1537 * allocate one and initialize to default values. | |
| 1538 */ | |
| 1539 imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS, | |
| 1540 M_WAITOK); | |
| 1541 | |
| 1542 if (imo == NULL) | |
| 1543 return (ENOBUFS); | |
| 1544 *imop = imo; | |
| 1545 imo->imo_multicast_ifp = NULL; | |
| 1546 imo->imo_multicast_addr.s_addr = INADDR_ANY; | |
| 1547 imo->imo_multicast_vif = -1; | |
| 1548 imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; | |
| 1549 imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; | |
| 1550 imo->imo_num_memberships = 0; | |
| 1551 } | |
| 1552 | |
| 1553 switch (sopt->sopt_name) { | |
| 1554 /* store an index number for the vif you wanna use in the send */ | |
| 1555 case IP_MULTICAST_VIF: | |
| 1556 if (legal_vif_num == 0) { | |
| 1557 error = EOPNOTSUPP; | |
| 1558 break; | |
| 1559 } | |
| 1560 error = sooptcopyin(sopt, &i, sizeof i, sizeof i); | |
| 1561 if (error) | |
| 1562 break; | |
| 1563 if (!legal_vif_num(i) && (i != -1)) { | |
| 1564 error = EINVAL; | |
| 1565 break; | |
| 1566 } | |
| 1567 imo->imo_multicast_vif = i; | |
| 1568 break; | |
| 1569 | |
| 1570 case IP_MULTICAST_IF: | |
| 1571 /* | |
| 1572 * Select the interface for outgoing multicast packets. | |
| 1573 */ | |
| 1574 error = sooptcopyin(sopt, &addr, sizeof addr, sizeof addr); | |
| 1575 if (error) | |
| 1576 break; | |
| 1577 /* | |
| 1578 * INADDR_ANY is used to remove a previous selection. | |
| 1579 * When no interface is selected, a default one is | |
| 1580 * chosen every time a multicast packet is sent. | |
| 1581 */ | |
| 1582 if (addr.s_addr == INADDR_ANY) { | |
| 1583 imo->imo_multicast_ifp = NULL; | |
| 1584 break; | |
| 1585 } | |
| 1586 /* | |
| 1587 * The selected interface is identified by its local | |
| 1588 * IP address. Find the interface and confirm that | |
| 1589 * it supports multicasting. | |
| 1590 */ | |
| 1591 s = splimp(); | |
| 1592 ifp = ip_multicast_if(&addr, &ifindex); | |
| 1593 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { | |
| 1594 splx(s); | |
| 1595 error = EADDRNOTAVAIL; | |
| 1596 break; | |
| 1597 } | |
| 1598 imo->imo_multicast_ifp = ifp; | |
| 1599 if (ifindex) | |
| 1600 imo->imo_multicast_addr = addr; | |
| 1601 else | |
| 1602 imo->imo_multicast_addr.s_addr = INADDR_ANY; | |
| 1603 splx(s); | |
| 1604 break; | |
| 1605 | |
| 1606 case IP_MULTICAST_TTL: | |
| 1607 /* | |
| 1608 * Set the IP time-to-live for outgoing multicast packets. | |
| 1609 * The original multicast API required a char argument, | |
| 1610 * which is inconsistent with the rest of the socket API. | |
| 1611 * We allow either a char or an int. | |
| 1612 */ | |
| 1613 if (sopt->sopt_valsize == 1) { | |
| 1614 u_char ttl; | |
| 1615 error = sooptcopyin(sopt, &ttl, 1, 1); | |
| 1616 if (error) | |
| 1617 break; | |
| 1618 imo->imo_multicast_ttl = ttl; | |
| 1619 } else { | |
| 1620 u_int ttl; | |
| 1621 error = sooptcopyin(sopt, &ttl, sizeof ttl, | |
| 1622 sizeof ttl); | |
| 1623 if (error) | |
| 1624 break; | |
| 1625 if (ttl > 255) | |
| 1626 error = EINVAL; | |
| 1627 else | |
| 1628 imo->imo_multicast_ttl = ttl; | |
| 1629 } | |
| 1630 break; | |
| 1631 | |
| 1632 case IP_MULTICAST_LOOP: | |
| 1633 /* | |
| 1634 * Set the loopback flag for outgoing multicast packets. | |
| 1635 * Must be zero or one. The original multicast API required a | |
| 1636 * char argument, which is inconsistent with the rest | |
| 1637 * of the socket API. We allow either a char or an int. | |
| 1638 */ | |
| 1639 if (sopt->sopt_valsize == 1) { | |
| 1640 u_char loop; | |
| 1641 error = sooptcopyin(sopt, &loop, 1, 1); | |
| 1642 if (error) | |
| 1643 break; | |
| 1644 imo->imo_multicast_loop = !!loop; | |
| 1645 } else { | |
| 1646 u_int loop; | |
| 1647 error = sooptcopyin(sopt, &loop, sizeof loop, | |
| 1648 sizeof loop); | |
| 1649 if (error) | |
| 1650 break; | |
| 1651 imo->imo_multicast_loop = !!loop; | |
| 1652 } | |
| 1653 break; | |
| 1654 | |
| 1655 case IP_ADD_MEMBERSHIP: | |
| 1656 /* | |
| 1657 * Add a multicast group membership. | |
| 1658 * Group must be a valid IP multicast address. | |
| 1659 */ | |
| 1660 error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq); | |
| 1661 if (error) | |
| 1662 break; | |
| 1663 | |
| 1664 if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) { | |
| 1665 error = EINVAL; | |
| 1666 break; | |
| 1667 } | |
| 1668 s = splimp(); | |
| 1669 /* | |
| 1670 * If no interface address was provided, use the interface of | |
| 1671 * the route to the given multicast address. | |
| 1672 */ | |
| 1673 if (mreq.imr_interface.s_addr == INADDR_ANY) { | |
| 1674 bzero((caddr_t)&ro, sizeof(ro)); | |
| 1675 dst = (struct sockaddr_in *)&ro.ro_dst; | |
| 1676 dst->sin_len = sizeof(*dst); | |
| 1677 dst->sin_family = AF_INET; | |
| 1678 dst->sin_addr = mreq.imr_multiaddr; | |
| 1679 rtalloc(&ro); | |
| 1680 if (ro.ro_rt == NULL) { | |
| 1681 error = EADDRNOTAVAIL; | |
| 1682 splx(s); | |
| 1683 break; | |
| 1684 } | |
| 1685 ifp = ro.ro_rt->rt_ifp; | |
| 1686 rtfree(ro.ro_rt); | |
| 1687 } | |
| 1688 else { | |
| 1689 ifp = ip_multicast_if(&mreq.imr_interface, NULL); | |
| 1690 } | |
| 1691 | |
| 1692 /* | |
| 1693 * See if we found an interface, and confirm that it | |
| 1694 * supports multicast. | |
| 1695 */ | |
| 1696 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { | |
| 1697 error = EADDRNOTAVAIL; | |
| 1698 splx(s); | |
| 1699 break; | |
| 1700 } | |
| 1701 /* | |
| 1702 * See if the membership already exists or if all the | |
| 1703 * membership slots are full. | |
| 1704 */ | |
| 1705 for (i = 0; i < imo->imo_num_memberships; ++i) { | |
| 1706 if (imo->imo_membership[i]->inm_ifp == ifp && | |
| 1707 imo->imo_membership[i]->inm_addr.s_addr | |
| 1708 == mreq.imr_multiaddr.s_addr) | |
| 1709 break; | |
| 1710 } | |
| 1711 if (i < imo->imo_num_memberships) { | |
| 1712 error = EADDRINUSE; | |
| 1713 splx(s); | |
| 1714 break; | |
| 1715 } | |
| 1716 if (i == IP_MAX_MEMBERSHIPS) { | |
| 1717 error = ETOOMANYREFS; | |
| 1718 splx(s); | |
| 1719 break; | |
| 1720 } | |
| 1721 /* | |
| 1722 * Everything looks good; add a new record to the multicast | |
| 1723 * address list for the given interface. | |
| 1724 */ | |
| 1725 if ((imo->imo_membership[i] = | |
| 1726 in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) { | |
| 1727 error = ENOBUFS; | |
| 1728 splx(s); | |
| 1729 break; | |
| 1730 } | |
| 1731 ++imo->imo_num_memberships; | |
| 1732 splx(s); | |
| 1733 break; | |
| 1734 | |
| 1735 case IP_DROP_MEMBERSHIP: | |
| 1736 /* | |
| 1737 * Drop a multicast group membership. | |
| 1738 * Group must be a valid IP multicast address. | |
| 1739 */ | |
| 1740 error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq); | |
| 1741 if (error) | |
| 1742 break; | |
| 1743 | |
| 1744 if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) { | |
| 1745 error = EINVAL; | |
| 1746 break; | |
| 1747 } | |
| 1748 | |
| 1749 s = splimp(); | |
| 1750 /* | |
| 1751 * If an interface address was specified, get a pointer | |
| 1752 * to its ifnet structure. | |
| 1753 */ | |
| 1754 if (mreq.imr_interface.s_addr == INADDR_ANY) | |
| 1755 ifp = NULL; | |
| 1756 else { | |
| 1757 ifp = ip_multicast_if(&mreq.imr_interface, NULL); | |
| 1758 if (ifp == NULL) { | |
| 1759 error = EADDRNOTAVAIL; | |
| 1760 splx(s); | |
| 1761 break; | |
| 1762 } | |
| 1763 } | |
| 1764 /* | |
| 1765 * Find the membership in the membership array. | |
| 1766 */ | |
| 1767 for (i = 0; i < imo->imo_num_memberships; ++i) { | |
| 1768 if ((ifp == NULL || | |
| 1769 imo->imo_membership[i]->inm_ifp == ifp) && | |
| 1770 imo->imo_membership[i]->inm_addr.s_addr == | |
| 1771 mreq.imr_multiaddr.s_addr) | |
| 1772 break; | |
| 1773 } | |
| 1774 if (i == imo->imo_num_memberships) { | |
| 1775 error = EADDRNOTAVAIL; | |
| 1776 splx(s); | |
| 1777 break; | |
| 1778 } | |
| 1779 /* | |
| 1780 * Give up the multicast address record to which the | |
| 1781 * membership points. | |
| 1782 */ | |
| 1783 in_delmulti(imo->imo_membership[i]); | |
| 1784 /* | |
| 1785 * Remove the gap in the membership array. | |
| 1786 */ | |
| 1787 for (++i; i < imo->imo_num_memberships; ++i) | |
| 1788 imo->imo_membership[i-1] = imo->imo_membership[i]; | |
| 1789 --imo->imo_num_memberships; | |
| 1790 splx(s); | |
| 1791 break; | |
| 1792 | |
| 1793 default: | |
| 1794 error = EOPNOTSUPP; | |
| 1795 break; | |
| 1796 } | |
| 1797 | |
| 1798 /* | |
| 1799 * If all options have default values, no need to keep the mbuf. | |
| 1800 */ | |
| 1801 if (imo->imo_multicast_ifp == NULL && | |
| 1802 imo->imo_multicast_vif == -1 && | |
| 1803 imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL && | |
| 1804 imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP && | |
| 1805 imo->imo_num_memberships == 0) { | |
| 1806 free(*imop, M_IPMOPTS); | |
| 1807 *imop = NULL; | |
| 1808 } | |
| 1809 | |
| 1810 return (error); | |
| 1811 } | |
| 1812 | |
| 1813 /* | |
| 1814 * Return the IP multicast options in response to user getsockopt(). | |
| 1815 */ | |
| 1816 static int | |
| 1817 ip_getmoptions(sopt, imo) | |
| 1818 struct sockopt *sopt; | |
| 1819 register struct ip_moptions *imo; | |
| 1820 { | |
| 1821 struct in_addr addr; | |
| 1822 struct in_ifaddr *ia; | |
| 1823 int error, optval; | |
| 1824 u_char coptval; | |
| 1825 | |
| 1826 error = 0; | |
| 1827 switch (sopt->sopt_name) { | |
| 1828 case IP_MULTICAST_VIF: | |
| 1829 if (imo != NULL) | |
| 1830 optval = imo->imo_multicast_vif; | |
| 1831 else | |
| 1832 optval = -1; | |
| 1833 error = sooptcopyout(sopt, &optval, sizeof optval); | |
| 1834 break; | |
| 1835 | |
| 1836 case IP_MULTICAST_IF: | |
| 1837 if (imo == NULL || imo->imo_multicast_ifp == NULL) | |
| 1838 addr.s_addr = INADDR_ANY; | |
| 1839 else if (imo->imo_multicast_addr.s_addr) { | |
| 1840 /* return the value user has set */ | |
| 1841 addr = imo->imo_multicast_addr; | |
| 1842 } else { | |
| 1843 IFP_TO_IA(imo->imo_multicast_ifp, ia); | |
| 1844 addr.s_addr = (ia == NULL) ? INADDR_ANY | |
| 1845 : IA_SIN(ia)->sin_addr.s_addr; | |
| 1846 } | |
| 1847 error = sooptcopyout(sopt, &addr, sizeof addr); | |
| 1848 break; | |
| 1849 | |
| 1850 case IP_MULTICAST_TTL: | |
| 1851 if (imo == 0) | |
| 1852 optval = coptval = IP_DEFAULT_MULTICAST_TTL; | |
| 1853 else | |
| 1854 optval = coptval = imo->imo_multicast_ttl; | |
| 1855 if (sopt->sopt_valsize == 1) | |
| 1856 error = sooptcopyout(sopt, &coptval, 1); | |
| 1857 else | |
| 1858 error = sooptcopyout(sopt, &optval, sizeof optval); | |
| 1859 break; | |
| 1860 | |
| 1861 case IP_MULTICAST_LOOP: | |
| 1862 if (imo == 0) | |
| 1863 optval = coptval = IP_DEFAULT_MULTICAST_LOOP; | |
| 1864 else | |
| 1865 optval = coptval = imo->imo_multicast_loop; | |
| 1866 if (sopt->sopt_valsize == 1) | |
| 1867 error = sooptcopyout(sopt, &coptval, 1); | |
| 1868 else | |
| 1869 error = sooptcopyout(sopt, &optval, sizeof optval); | |
| 1870 break; | |
| 1871 | |
| 1872 default: | |
| 1873 error = ENOPROTOOPT; | |
| 1874 break; | |
| 1875 } | |
| 1876 return (error); | |
| 1877 } | |
| 1878 | |
| 1879 /* | |
| 1880 * Discard the IP multicast options. | |
| 1881 */ | |
| 1882 void | |
| 1883 ip_freemoptions(imo) | |
| 1884 register struct ip_moptions *imo; | |
| 1885 { | |
| 1886 register int i; | |
| 1887 | |
| 1888 if (imo != NULL) { | |
| 1889 for (i = 0; i < imo->imo_num_memberships; ++i) | |
| 1890 in_delmulti(imo->imo_membership[i]); | |
| 1891 free(imo, M_IPMOPTS); | |
| 1892 } | |
| 1893 } | |
| 1894 | |
| 1895 /* | |
| 1896 * Routine called from ip_output() to loop back a copy of an IP multicast | |
| 1897 * packet to the input queue of a specified interface. Note that this | |
| 1898 * calls the output routine of the loopback "driver", but with an interface | |
| 1899 * pointer that might NOT be a loopback interface -- evil, but easier than | |
| 1900 * replicating that code here. | |
| 1901 */ | |
| 1902 static void | |
| 1903 ip_mloopback(ifp, m, dst, hlen) | |
| 1904 struct ifnet *ifp; | |
| 1905 register struct mbuf *m; | |
| 1906 register struct sockaddr_in *dst; | |
| 1907 int hlen; | |
| 1908 { | |
| 1909 register struct ip *ip; | |
| 1910 struct mbuf *copym; | |
| 1911 | |
| 1912 copym = m_copy(m, 0, M_COPYALL); | |
| 1913 if (copym != NULL && (copym->m_flags & M_EXT || copym->m_len < hlen)) | |
| 1914 copym = m_pullup(copym, hlen); | |
| 1915 if (copym != NULL) { | |
| 1916 /* | |
| 1917 * We don't bother to fragment if the IP length is greater | |
| 1918 * than the interface's MTU. Can this possibly matter? | |
| 1919 */ | |
| 1920 ip = mtod(copym, struct ip *); | |
| 1921 HTONS(ip->ip_len); | |
| 1922 HTONS(ip->ip_off); | |
| 1923 ip->ip_sum = 0; | |
| 1924 if (ip->ip_vhl == IP_VHL_BORING) { | |
| 1925 ip->ip_sum = in_cksum_hdr(ip); | |
| 1926 } else { | |
| 1927 ip->ip_sum = in_cksum(copym, hlen); | |
| 1928 } | |
| 1929 /* | |
| 1930 * NB: | |
| 1931 * It's not clear whether there are any lingering | |
| 1932 * reentrancy problems in other areas which might | |
| 1933 * be exposed by using ip_input directly (in | |
| 1934 * particular, everything which modifies the packet | |
| 1935 * in-place). Yet another option is using the | |
| 1936 * protosw directly to deliver the looped back | |
| 1937 * packet. For the moment, we'll err on the side | |
| 1938 * of safety by using if_simloop(). | |
| 1939 */ | |
| 1940 #if 1 /* XXX */ | |
| 1941 if (dst->sin_family != AF_INET) { | |
| 1942 printf("ip_mloopback: bad address family %d\n", | |
| 1943 dst->sin_family); | |
| 1944 dst->sin_family = AF_INET; | |
| 1945 } | |
| 1946 #endif | |
| 1947 | |
| 1948 #ifdef notdef | |
| 1949 copym->m_pkthdr.rcvif = ifp; | |
| 1950 ip_input(copym); | |
| 1951 #else | |
| 1952 /* if the checksum hasn't been computed, mark it as valid */ | |
| 1953 if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { | |
| 1954 copym->m_pkthdr.csum_flags |= | |
| 1955 CSUM_DATA_VALID | CSUM_PSEUDO_HDR; | |
| 1956 copym->m_pkthdr.csum_data = 0xffff; | |
| 1957 } | |
| 1958 if_simloop(ifp, copym, dst->sin_family, 0); | |
| 1959 #endif | |
| 1960 } | |
| 1961 } |
