1df8bae1dSRodney W. Grimes /* 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1988, 1990, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 33df8bae1dSRodney W. Grimes * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 34ce8c72b1SPeter Wemm * $Id: ip_output.c,v 1.37 1996/05/06 17:42:13 wollman Exp $ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 379c9137eaSGarrett Wollman #define _IP_VHL 389c9137eaSGarrett Wollman 39df8bae1dSRodney W. Grimes #include <sys/param.h> 402ee45d7dSDavid Greenman #include <sys/queue.h> 4126f9a767SRodney W. Grimes #include <sys/systm.h> 42df8bae1dSRodney W. Grimes #include <sys/malloc.h> 43df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 44df8bae1dSRodney W. Grimes #include <sys/errno.h> 45df8bae1dSRodney W. Grimes #include <sys/protosw.h> 46df8bae1dSRodney W. Grimes #include <sys/socket.h> 47df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 48df8bae1dSRodney W. Grimes 49df8bae1dSRodney W. Grimes #include <net/if.h> 50df8bae1dSRodney W. Grimes #include <net/route.h> 51df8bae1dSRodney W. Grimes 52df8bae1dSRodney W. Grimes #include <netinet/in.h> 53df8bae1dSRodney W. Grimes #include <netinet/in_systm.h> 54df8bae1dSRodney W. Grimes #include <netinet/ip.h> 55df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h> 56df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 57df8bae1dSRodney W. Grimes #include <netinet/ip_var.h> 58df8bae1dSRodney W. Grimes 59df8bae1dSRodney W. Grimes #ifdef vax 60df8bae1dSRodney W. Grimes #include <machine/mtpr.h> 61df8bae1dSRodney W. Grimes #endif 629c9137eaSGarrett Wollman #include <machine/in_cksum.h> 63df8bae1dSRodney W. Grimes 64f23b4c91SGarrett Wollman u_short ip_id; 65f23b4c91SGarrett Wollman 66df8bae1dSRodney W. Grimes static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); 67df8bae1dSRodney W. Grimes static void ip_mloopback 68df8bae1dSRodney W. Grimes __P((struct ifnet *, struct mbuf *, struct sockaddr_in *)); 690312fbe9SPoul-Henning Kamp static int ip_getmoptions 700312fbe9SPoul-Henning Kamp __P((int, struct ip_moptions *, struct mbuf **)); 710312fbe9SPoul-Henning Kamp static int ip_optcopy __P((struct ip *, struct ip *)); 720312fbe9SPoul-Henning Kamp static int ip_pcbopts __P((struct mbuf **, struct mbuf *)); 730312fbe9SPoul-Henning Kamp static int ip_setmoptions 740312fbe9SPoul-Henning Kamp __P((int, struct ip_moptions **, struct mbuf *)); 75df8bae1dSRodney W. Grimes 76df8bae1dSRodney W. Grimes /* 77df8bae1dSRodney W. Grimes * IP output. The packet in mbuf chain m contains a skeletal IP 78df8bae1dSRodney W. Grimes * header (with len, off, ttl, proto, tos, src, dst). 79df8bae1dSRodney W. Grimes * The mbuf chain containing the packet will be freed. 80df8bae1dSRodney W. Grimes * The mbuf opt, if present, will not be freed. 81df8bae1dSRodney W. Grimes */ 82df8bae1dSRodney W. Grimes int 83df8bae1dSRodney W. Grimes ip_output(m0, opt, ro, flags, imo) 84df8bae1dSRodney W. Grimes struct mbuf *m0; 85df8bae1dSRodney W. Grimes struct mbuf *opt; 86df8bae1dSRodney W. Grimes struct route *ro; 87df8bae1dSRodney W. Grimes int flags; 88df8bae1dSRodney W. Grimes struct ip_moptions *imo; 89df8bae1dSRodney W. Grimes { 9023bf9953SPoul-Henning Kamp struct ip *ip, *mhip; 9123bf9953SPoul-Henning Kamp struct ifnet *ifp; 9223bf9953SPoul-Henning Kamp struct mbuf *m = m0; 9323bf9953SPoul-Henning Kamp int hlen = sizeof (struct ip); 94df8bae1dSRodney W. Grimes int len, off, error = 0; 95df8bae1dSRodney W. Grimes struct sockaddr_in *dst; 96df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 979f9b3dc4SGarrett Wollman int isbroadcast; 98df8bae1dSRodney W. Grimes 99df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 100df8bae1dSRodney W. Grimes if ((m->m_flags & M_PKTHDR) == 0) 101df8bae1dSRodney W. Grimes panic("ip_output no HDR"); 1029c9137eaSGarrett Wollman if (!ro) 1039c9137eaSGarrett Wollman panic("ip_output no route, proto = %d", 1049c9137eaSGarrett Wollman mtod(m, struct ip *)->ip_p); 105df8bae1dSRodney W. Grimes #endif 106df8bae1dSRodney W. Grimes if (opt) { 107df8bae1dSRodney W. Grimes m = ip_insertoptions(m, opt, &len); 108df8bae1dSRodney W. Grimes hlen = len; 109df8bae1dSRodney W. Grimes } 110df8bae1dSRodney W. Grimes ip = mtod(m, struct ip *); 111df8bae1dSRodney W. Grimes /* 112df8bae1dSRodney W. Grimes * Fill in IP header. 113df8bae1dSRodney W. Grimes */ 114df8bae1dSRodney W. Grimes if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { 1159c9137eaSGarrett Wollman ip->ip_vhl = IP_MAKE_VHL(IPVERSION, hlen >> 2); 116df8bae1dSRodney W. Grimes ip->ip_off &= IP_DF; 117df8bae1dSRodney W. Grimes ip->ip_id = htons(ip_id++); 118df8bae1dSRodney W. Grimes ipstat.ips_localout++; 119df8bae1dSRodney W. Grimes } else { 1209c9137eaSGarrett Wollman hlen = IP_VHL_HL(ip->ip_vhl) << 2; 121df8bae1dSRodney W. Grimes } 1229c9137eaSGarrett Wollman 123df8bae1dSRodney W. Grimes dst = (struct sockaddr_in *)&ro->ro_dst; 124df8bae1dSRodney W. Grimes /* 125df8bae1dSRodney W. Grimes * If there is a cached route, 126df8bae1dSRodney W. Grimes * check that it is to the same destination 127df8bae1dSRodney W. Grimes * and is still up. If not, free it and try again. 128df8bae1dSRodney W. Grimes */ 129df8bae1dSRodney W. Grimes if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 130df8bae1dSRodney W. Grimes dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { 131df8bae1dSRodney W. Grimes RTFREE(ro->ro_rt); 132df8bae1dSRodney W. Grimes ro->ro_rt = (struct rtentry *)0; 133df8bae1dSRodney W. Grimes } 134df8bae1dSRodney W. Grimes if (ro->ro_rt == 0) { 135df8bae1dSRodney W. Grimes dst->sin_family = AF_INET; 136df8bae1dSRodney W. Grimes dst->sin_len = sizeof(*dst); 137df8bae1dSRodney W. Grimes dst->sin_addr = ip->ip_dst; 138df8bae1dSRodney W. Grimes } 139df8bae1dSRodney W. Grimes /* 140df8bae1dSRodney W. Grimes * If routing to interface only, 141df8bae1dSRodney W. Grimes * short circuit routing lookup. 142df8bae1dSRodney W. Grimes */ 143df8bae1dSRodney W. Grimes #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 144df8bae1dSRodney W. Grimes #define sintosa(sin) ((struct sockaddr *)(sin)) 145df8bae1dSRodney W. Grimes if (flags & IP_ROUTETOIF) { 146df8bae1dSRodney W. Grimes if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 && 147df8bae1dSRodney W. Grimes (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) { 148df8bae1dSRodney W. Grimes ipstat.ips_noroute++; 149df8bae1dSRodney W. Grimes error = ENETUNREACH; 150df8bae1dSRodney W. Grimes goto bad; 151df8bae1dSRodney W. Grimes } 152df8bae1dSRodney W. Grimes ifp = ia->ia_ifp; 153df8bae1dSRodney W. Grimes ip->ip_ttl = 1; 1549f9b3dc4SGarrett Wollman isbroadcast = in_broadcast(dst->sin_addr, ifp); 155df8bae1dSRodney W. Grimes } else { 1562c17fe93SGarrett Wollman /* 1572c17fe93SGarrett Wollman * If this is the case, we probably don't want to allocate 1582c17fe93SGarrett Wollman * a protocol-cloned route since we didn't get one from the 1592c17fe93SGarrett Wollman * ULP. This lets TCP do its thing, while not burdening 1602c17fe93SGarrett Wollman * forwarding or ICMP with the overhead of cloning a route. 1612c17fe93SGarrett Wollman * Of course, we still want to do any cloning requested by 1622c17fe93SGarrett Wollman * the link layer, as this is probably required in all cases 1632c17fe93SGarrett Wollman * for correct operation (as it is for ARP). 1642c17fe93SGarrett Wollman */ 165df8bae1dSRodney W. Grimes if (ro->ro_rt == 0) 1662c17fe93SGarrett Wollman rtalloc_ign(ro, RTF_PRCLONING); 167df8bae1dSRodney W. Grimes if (ro->ro_rt == 0) { 168df8bae1dSRodney W. Grimes ipstat.ips_noroute++; 169df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 170df8bae1dSRodney W. Grimes goto bad; 171df8bae1dSRodney W. Grimes } 172df8bae1dSRodney W. Grimes ia = ifatoia(ro->ro_rt->rt_ifa); 173df8bae1dSRodney W. Grimes ifp = ro->ro_rt->rt_ifp; 174df8bae1dSRodney W. Grimes ro->ro_rt->rt_use++; 175df8bae1dSRodney W. Grimes if (ro->ro_rt->rt_flags & RTF_GATEWAY) 176df8bae1dSRodney W. Grimes dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; 1779f9b3dc4SGarrett Wollman if (ro->ro_rt->rt_flags & RTF_HOST) 1789f9b3dc4SGarrett Wollman isbroadcast = (ro->ro_rt->rt_flags & RTF_BROADCAST); 1799f9b3dc4SGarrett Wollman else 1809f9b3dc4SGarrett Wollman isbroadcast = in_broadcast(dst->sin_addr, ifp); 181df8bae1dSRodney W. Grimes } 182df8bae1dSRodney W. Grimes if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { 183df8bae1dSRodney W. Grimes struct in_multi *inm; 184df8bae1dSRodney W. Grimes 185df8bae1dSRodney W. Grimes m->m_flags |= M_MCAST; 186df8bae1dSRodney W. Grimes /* 187df8bae1dSRodney W. Grimes * IP destination address is multicast. Make sure "dst" 188df8bae1dSRodney W. Grimes * still points to the address in "ro". (It may have been 189df8bae1dSRodney W. Grimes * changed to point to a gateway address, above.) 190df8bae1dSRodney W. Grimes */ 191df8bae1dSRodney W. Grimes dst = (struct sockaddr_in *)&ro->ro_dst; 192df8bae1dSRodney W. Grimes /* 193df8bae1dSRodney W. Grimes * See if the caller provided any multicast options 194df8bae1dSRodney W. Grimes */ 195df8bae1dSRodney W. Grimes if (imo != NULL) { 196df8bae1dSRodney W. Grimes ip->ip_ttl = imo->imo_multicast_ttl; 197df8bae1dSRodney W. Grimes if (imo->imo_multicast_ifp != NULL) 198df8bae1dSRodney W. Grimes ifp = imo->imo_multicast_ifp; 1991c5de19aSGarrett Wollman if (imo->imo_multicast_vif != -1) 2001c5de19aSGarrett Wollman ip->ip_src.s_addr = 2011c5de19aSGarrett Wollman ip_mcast_src(imo->imo_multicast_vif); 202df8bae1dSRodney W. Grimes } else 203df8bae1dSRodney W. Grimes ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; 204df8bae1dSRodney W. Grimes /* 205df8bae1dSRodney W. Grimes * Confirm that the outgoing interface supports multicast. 206df8bae1dSRodney W. Grimes */ 2071c5de19aSGarrett Wollman if ((imo == NULL) || (imo->imo_multicast_vif == -1)) { 208df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_MULTICAST) == 0) { 209df8bae1dSRodney W. Grimes ipstat.ips_noroute++; 210df8bae1dSRodney W. Grimes error = ENETUNREACH; 211df8bae1dSRodney W. Grimes goto bad; 212df8bae1dSRodney W. Grimes } 2131c5de19aSGarrett Wollman } 214df8bae1dSRodney W. Grimes /* 215df8bae1dSRodney W. Grimes * If source address not specified yet, use address 216df8bae1dSRodney W. Grimes * of outgoing interface. 217df8bae1dSRodney W. Grimes */ 218df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr == INADDR_ANY) { 219df8bae1dSRodney W. Grimes register struct in_ifaddr *ia; 220df8bae1dSRodney W. Grimes 221df8bae1dSRodney W. Grimes for (ia = in_ifaddr; ia; ia = ia->ia_next) 222df8bae1dSRodney W. Grimes if (ia->ia_ifp == ifp) { 223df8bae1dSRodney W. Grimes ip->ip_src = IA_SIN(ia)->sin_addr; 224df8bae1dSRodney W. Grimes break; 225df8bae1dSRodney W. Grimes } 226df8bae1dSRodney W. Grimes } 227df8bae1dSRodney W. Grimes 228df8bae1dSRodney W. Grimes IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); 229df8bae1dSRodney W. Grimes if (inm != NULL && 230df8bae1dSRodney W. Grimes (imo == NULL || imo->imo_multicast_loop)) { 231df8bae1dSRodney W. Grimes /* 232df8bae1dSRodney W. Grimes * If we belong to the destination multicast group 233df8bae1dSRodney W. Grimes * on the outgoing interface, and the caller did not 234df8bae1dSRodney W. Grimes * forbid loopback, loop back a copy. 235df8bae1dSRodney W. Grimes */ 236df8bae1dSRodney W. Grimes ip_mloopback(ifp, m, dst); 237df8bae1dSRodney W. Grimes } 238df8bae1dSRodney W. Grimes else { 239df8bae1dSRodney W. Grimes /* 240df8bae1dSRodney W. Grimes * If we are acting as a multicast router, perform 241df8bae1dSRodney W. Grimes * multicast forwarding as if the packet had just 242df8bae1dSRodney W. Grimes * arrived on the interface to which we are about 243df8bae1dSRodney W. Grimes * to send. The multicast forwarding function 244df8bae1dSRodney W. Grimes * recursively calls this function, using the 245df8bae1dSRodney W. Grimes * IP_FORWARDING flag to prevent infinite recursion. 246df8bae1dSRodney W. Grimes * 247df8bae1dSRodney W. Grimes * Multicasts that are looped back by ip_mloopback(), 248df8bae1dSRodney W. Grimes * above, will be forwarded by the ip_input() routine, 249df8bae1dSRodney W. Grimes * if necessary. 250df8bae1dSRodney W. Grimes */ 251df8bae1dSRodney W. Grimes if (ip_mrouter && (flags & IP_FORWARDING) == 0) { 252f0068c4aSGarrett Wollman /* 253f0068c4aSGarrett Wollman * Check if rsvp daemon is running. If not, don't 254f0068c4aSGarrett Wollman * set ip_moptions. This ensures that the packet 255f0068c4aSGarrett Wollman * is multicast and not just sent down one link 256f0068c4aSGarrett Wollman * as prescribed by rsvpd. 257f0068c4aSGarrett Wollman */ 258b124e4f2SGarrett Wollman if (!rsvp_on) 259f0068c4aSGarrett Wollman imo = NULL; 260f0068c4aSGarrett Wollman if (ip_mforward(ip, ifp, m, imo) != 0) { 261df8bae1dSRodney W. Grimes m_freem(m); 262df8bae1dSRodney W. Grimes goto done; 263df8bae1dSRodney W. Grimes } 264df8bae1dSRodney W. Grimes } 265df8bae1dSRodney W. Grimes } 2665e9ae478SGarrett Wollman 267df8bae1dSRodney W. Grimes /* 268df8bae1dSRodney W. Grimes * Multicasts with a time-to-live of zero may be looped- 269df8bae1dSRodney W. Grimes * back, above, but must not be transmitted on a network. 270df8bae1dSRodney W. Grimes * Also, multicasts addressed to the loopback interface 271df8bae1dSRodney W. Grimes * are not sent -- the above call to ip_mloopback() will 272df8bae1dSRodney W. Grimes * loop back a copy if this host actually belongs to the 273df8bae1dSRodney W. Grimes * destination group on the loopback interface. 274df8bae1dSRodney W. Grimes */ 275f5fea3ddSPaul Traina if (ip->ip_ttl == 0 || ifp->if_flags & IFF_LOOPBACK) { 276df8bae1dSRodney W. Grimes m_freem(m); 277df8bae1dSRodney W. Grimes goto done; 278df8bae1dSRodney W. Grimes } 279df8bae1dSRodney W. Grimes 280df8bae1dSRodney W. Grimes goto sendit; 281df8bae1dSRodney W. Grimes } 282df8bae1dSRodney W. Grimes #ifndef notdef 283df8bae1dSRodney W. Grimes /* 284df8bae1dSRodney W. Grimes * If source address not specified yet, use address 285df8bae1dSRodney W. Grimes * of outgoing interface. 286df8bae1dSRodney W. Grimes */ 287df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr == INADDR_ANY) 288df8bae1dSRodney W. Grimes ip->ip_src = IA_SIN(ia)->sin_addr; 289df8bae1dSRodney W. Grimes #endif 290df8bae1dSRodney W. Grimes /* 291b5390296SDavid Greenman * Verify that we have any chance at all of being able to queue 292b5390296SDavid Greenman * the packet or packet fragments 293b5390296SDavid Greenman */ 294b5390296SDavid Greenman if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >= 295b5390296SDavid Greenman ifp->if_snd.ifq_maxlen) { 296b5390296SDavid Greenman error = ENOBUFS; 297b5390296SDavid Greenman goto bad; 298b5390296SDavid Greenman } 299b5390296SDavid Greenman 300b5390296SDavid Greenman /* 301df8bae1dSRodney W. Grimes * Look for broadcast address and 302df8bae1dSRodney W. Grimes * and verify user is allowed to send 303df8bae1dSRodney W. Grimes * such a packet. 304df8bae1dSRodney W. Grimes */ 3059f9b3dc4SGarrett Wollman if (isbroadcast) { 306df8bae1dSRodney W. Grimes if ((ifp->if_flags & IFF_BROADCAST) == 0) { 307df8bae1dSRodney W. Grimes error = EADDRNOTAVAIL; 308df8bae1dSRodney W. Grimes goto bad; 309df8bae1dSRodney W. Grimes } 310df8bae1dSRodney W. Grimes if ((flags & IP_ALLOWBROADCAST) == 0) { 311df8bae1dSRodney W. Grimes error = EACCES; 312df8bae1dSRodney W. Grimes goto bad; 313df8bae1dSRodney W. Grimes } 314df8bae1dSRodney W. Grimes /* don't allow broadcast messages to be fragmented */ 315df8bae1dSRodney W. Grimes if ((u_short)ip->ip_len > ifp->if_mtu) { 316df8bae1dSRodney W. Grimes error = EMSGSIZE; 317df8bae1dSRodney W. Grimes goto bad; 318df8bae1dSRodney W. Grimes } 319df8bae1dSRodney W. Grimes m->m_flags |= M_BCAST; 3209f9b3dc4SGarrett Wollman } else { 321df8bae1dSRodney W. Grimes m->m_flags &= ~M_BCAST; 3229f9b3dc4SGarrett Wollman } 323df8bae1dSRodney W. Grimes 324df8bae1dSRodney W. Grimes sendit: 325df8bae1dSRodney W. Grimes /* 326e7319babSPoul-Henning Kamp * Check with the firewall... 327e7319babSPoul-Henning Kamp */ 32823bf9953SPoul-Henning Kamp if (ip_fw_chk_ptr && !(*ip_fw_chk_ptr)(&ip, hlen, ifp, 1, &m)) { 329b83e4314SPoul-Henning Kamp error = EACCES; 330e7319babSPoul-Henning Kamp goto done; 331e7319babSPoul-Henning Kamp } 332e7319babSPoul-Henning Kamp 333e7319babSPoul-Henning Kamp /* 334df8bae1dSRodney W. Grimes * If small enough for interface, can just send directly. 335df8bae1dSRodney W. Grimes */ 336df8bae1dSRodney W. Grimes if ((u_short)ip->ip_len <= ifp->if_mtu) { 337df8bae1dSRodney W. Grimes ip->ip_len = htons((u_short)ip->ip_len); 338df8bae1dSRodney W. Grimes ip->ip_off = htons((u_short)ip->ip_off); 339df8bae1dSRodney W. Grimes ip->ip_sum = 0; 3409c9137eaSGarrett Wollman if (ip->ip_vhl == IP_VHL_BORING) { 3419c9137eaSGarrett Wollman ip->ip_sum = in_cksum_hdr(ip); 3429c9137eaSGarrett Wollman } else { 343df8bae1dSRodney W. Grimes ip->ip_sum = in_cksum(m, hlen); 3449c9137eaSGarrett Wollman } 345df8bae1dSRodney W. Grimes error = (*ifp->if_output)(ifp, m, 346df8bae1dSRodney W. Grimes (struct sockaddr *)dst, ro->ro_rt); 347df8bae1dSRodney W. Grimes goto done; 348df8bae1dSRodney W. Grimes } 349df8bae1dSRodney W. Grimes /* 350df8bae1dSRodney W. Grimes * Too large for interface; fragment if possible. 351df8bae1dSRodney W. Grimes * Must be able to put at least 8 bytes per fragment. 352df8bae1dSRodney W. Grimes */ 353df8bae1dSRodney W. Grimes if (ip->ip_off & IP_DF) { 354df8bae1dSRodney W. Grimes error = EMSGSIZE; 3553d1f141bSGarrett Wollman /* 3563d1f141bSGarrett Wollman * This case can happen if the user changed the MTU 3573d1f141bSGarrett Wollman * of an interface after enabling IP on it. Because 3583d1f141bSGarrett Wollman * most netifs don't keep track of routes pointing to 3593d1f141bSGarrett Wollman * them, there is no way for one to update all its 3603d1f141bSGarrett Wollman * routes when the MTU is changed. 3613d1f141bSGarrett Wollman */ 3623d1f141bSGarrett Wollman if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) 3633d1f141bSGarrett Wollman && !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU) 3643d1f141bSGarrett Wollman && (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) { 3653d1f141bSGarrett Wollman ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu; 3663d1f141bSGarrett Wollman } 367df8bae1dSRodney W. Grimes ipstat.ips_cantfrag++; 368df8bae1dSRodney W. Grimes goto bad; 369df8bae1dSRodney W. Grimes } 370df8bae1dSRodney W. Grimes len = (ifp->if_mtu - hlen) &~ 7; 371df8bae1dSRodney W. Grimes if (len < 8) { 372df8bae1dSRodney W. Grimes error = EMSGSIZE; 373df8bae1dSRodney W. Grimes goto bad; 374df8bae1dSRodney W. Grimes } 375df8bae1dSRodney W. Grimes 376df8bae1dSRodney W. Grimes { 377df8bae1dSRodney W. Grimes int mhlen, firstlen = len; 378df8bae1dSRodney W. Grimes struct mbuf **mnext = &m->m_nextpkt; 379df8bae1dSRodney W. Grimes 380df8bae1dSRodney W. Grimes /* 381df8bae1dSRodney W. Grimes * Loop through length of segment after first fragment, 382df8bae1dSRodney W. Grimes * make new header and copy data of each part and link onto chain. 383df8bae1dSRodney W. Grimes */ 384df8bae1dSRodney W. Grimes m0 = m; 385df8bae1dSRodney W. Grimes mhlen = sizeof (struct ip); 386df8bae1dSRodney W. Grimes for (off = hlen + len; off < (u_short)ip->ip_len; off += len) { 387df8bae1dSRodney W. Grimes MGETHDR(m, M_DONTWAIT, MT_HEADER); 388df8bae1dSRodney W. Grimes if (m == 0) { 389df8bae1dSRodney W. Grimes error = ENOBUFS; 390df8bae1dSRodney W. Grimes ipstat.ips_odropped++; 391df8bae1dSRodney W. Grimes goto sendorfree; 392df8bae1dSRodney W. Grimes } 393df8bae1dSRodney W. Grimes m->m_data += max_linkhdr; 394df8bae1dSRodney W. Grimes mhip = mtod(m, struct ip *); 395df8bae1dSRodney W. Grimes *mhip = *ip; 396df8bae1dSRodney W. Grimes if (hlen > sizeof (struct ip)) { 397df8bae1dSRodney W. Grimes mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); 3989c9137eaSGarrett Wollman mhip->ip_vhl = IP_MAKE_VHL(IPVERSION, mhlen >> 2); 399df8bae1dSRodney W. Grimes } 400df8bae1dSRodney W. Grimes m->m_len = mhlen; 401df8bae1dSRodney W. Grimes mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); 402df8bae1dSRodney W. Grimes if (ip->ip_off & IP_MF) 403df8bae1dSRodney W. Grimes mhip->ip_off |= IP_MF; 404df8bae1dSRodney W. Grimes if (off + len >= (u_short)ip->ip_len) 405df8bae1dSRodney W. Grimes len = (u_short)ip->ip_len - off; 406df8bae1dSRodney W. Grimes else 407df8bae1dSRodney W. Grimes mhip->ip_off |= IP_MF; 408df8bae1dSRodney W. Grimes mhip->ip_len = htons((u_short)(len + mhlen)); 409df8bae1dSRodney W. Grimes m->m_next = m_copy(m0, off, len); 410df8bae1dSRodney W. Grimes if (m->m_next == 0) { 411df8bae1dSRodney W. Grimes (void) m_free(m); 412df8bae1dSRodney W. Grimes error = ENOBUFS; /* ??? */ 413df8bae1dSRodney W. Grimes ipstat.ips_odropped++; 414df8bae1dSRodney W. Grimes goto sendorfree; 415df8bae1dSRodney W. Grimes } 416df8bae1dSRodney W. Grimes m->m_pkthdr.len = mhlen + len; 417df8bae1dSRodney W. Grimes m->m_pkthdr.rcvif = (struct ifnet *)0; 418df8bae1dSRodney W. Grimes mhip->ip_off = htons((u_short)mhip->ip_off); 419df8bae1dSRodney W. Grimes mhip->ip_sum = 0; 4209c9137eaSGarrett Wollman if (mhip->ip_vhl == IP_VHL_BORING) { 421e2184122SBruce Evans mhip->ip_sum = in_cksum_hdr(mhip); 4229c9137eaSGarrett Wollman } else { 423df8bae1dSRodney W. Grimes mhip->ip_sum = in_cksum(m, mhlen); 4249c9137eaSGarrett Wollman } 425df8bae1dSRodney W. Grimes *mnext = m; 426df8bae1dSRodney W. Grimes mnext = &m->m_nextpkt; 427df8bae1dSRodney W. Grimes ipstat.ips_ofragments++; 428df8bae1dSRodney W. Grimes } 429df8bae1dSRodney W. Grimes /* 430df8bae1dSRodney W. Grimes * Update first fragment by trimming what's been copied out 431df8bae1dSRodney W. Grimes * and updating header, then send each fragment (in order). 432df8bae1dSRodney W. Grimes */ 433df8bae1dSRodney W. Grimes m = m0; 434df8bae1dSRodney W. Grimes m_adj(m, hlen + firstlen - (u_short)ip->ip_len); 435df8bae1dSRodney W. Grimes m->m_pkthdr.len = hlen + firstlen; 436df8bae1dSRodney W. Grimes ip->ip_len = htons((u_short)m->m_pkthdr.len); 437df8bae1dSRodney W. Grimes ip->ip_off = htons((u_short)(ip->ip_off | IP_MF)); 438df8bae1dSRodney W. Grimes ip->ip_sum = 0; 4399c9137eaSGarrett Wollman if (ip->ip_vhl == IP_VHL_BORING) { 4409c9137eaSGarrett Wollman ip->ip_sum = in_cksum_hdr(ip); 4419c9137eaSGarrett Wollman } else { 442df8bae1dSRodney W. Grimes ip->ip_sum = in_cksum(m, hlen); 4439c9137eaSGarrett Wollman } 444df8bae1dSRodney W. Grimes sendorfree: 445df8bae1dSRodney W. Grimes for (m = m0; m; m = m0) { 446df8bae1dSRodney W. Grimes m0 = m->m_nextpkt; 447df8bae1dSRodney W. Grimes m->m_nextpkt = 0; 448df8bae1dSRodney W. Grimes if (error == 0) 449df8bae1dSRodney W. Grimes error = (*ifp->if_output)(ifp, m, 450df8bae1dSRodney W. Grimes (struct sockaddr *)dst, ro->ro_rt); 451df8bae1dSRodney W. Grimes else 452df8bae1dSRodney W. Grimes m_freem(m); 453df8bae1dSRodney W. Grimes } 454df8bae1dSRodney W. Grimes 455df8bae1dSRodney W. Grimes if (error == 0) 456df8bae1dSRodney W. Grimes ipstat.ips_fragmented++; 457df8bae1dSRodney W. Grimes } 458df8bae1dSRodney W. Grimes done: 459df8bae1dSRodney W. Grimes return (error); 460df8bae1dSRodney W. Grimes bad: 461df8bae1dSRodney W. Grimes m_freem(m0); 462df8bae1dSRodney W. Grimes goto done; 463df8bae1dSRodney W. Grimes } 464df8bae1dSRodney W. Grimes 465df8bae1dSRodney W. Grimes /* 466df8bae1dSRodney W. Grimes * Insert IP options into preformed packet. 467df8bae1dSRodney W. Grimes * Adjust IP destination as required for IP source routing, 468df8bae1dSRodney W. Grimes * as indicated by a non-zero in_addr at the start of the options. 469072b9b24SPaul Traina * 470072b9b24SPaul Traina * XXX This routine assumes that the packet has no options in place. 471df8bae1dSRodney W. Grimes */ 472df8bae1dSRodney W. Grimes static struct mbuf * 473df8bae1dSRodney W. Grimes ip_insertoptions(m, opt, phlen) 474df8bae1dSRodney W. Grimes register struct mbuf *m; 475df8bae1dSRodney W. Grimes struct mbuf *opt; 476df8bae1dSRodney W. Grimes int *phlen; 477df8bae1dSRodney W. Grimes { 478df8bae1dSRodney W. Grimes register struct ipoption *p = mtod(opt, struct ipoption *); 479df8bae1dSRodney W. Grimes struct mbuf *n; 480df8bae1dSRodney W. Grimes register struct ip *ip = mtod(m, struct ip *); 481df8bae1dSRodney W. Grimes unsigned optlen; 482df8bae1dSRodney W. Grimes 483df8bae1dSRodney W. Grimes optlen = opt->m_len - sizeof(p->ipopt_dst); 484df8bae1dSRodney W. Grimes if (optlen + (u_short)ip->ip_len > IP_MAXPACKET) 485df8bae1dSRodney W. Grimes return (m); /* XXX should fail */ 486df8bae1dSRodney W. Grimes if (p->ipopt_dst.s_addr) 487df8bae1dSRodney W. Grimes ip->ip_dst = p->ipopt_dst; 488df8bae1dSRodney W. Grimes if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) { 489df8bae1dSRodney W. Grimes MGETHDR(n, M_DONTWAIT, MT_HEADER); 490df8bae1dSRodney W. Grimes if (n == 0) 491df8bae1dSRodney W. Grimes return (m); 492df8bae1dSRodney W. Grimes n->m_pkthdr.len = m->m_pkthdr.len + optlen; 493df8bae1dSRodney W. Grimes m->m_len -= sizeof(struct ip); 494df8bae1dSRodney W. Grimes m->m_data += sizeof(struct ip); 495df8bae1dSRodney W. Grimes n->m_next = m; 496df8bae1dSRodney W. Grimes m = n; 497df8bae1dSRodney W. Grimes m->m_len = optlen + sizeof(struct ip); 498df8bae1dSRodney W. Grimes m->m_data += max_linkhdr; 49994a5d9b6SDavid Greenman (void)memcpy(mtod(m, void *), ip, sizeof(struct ip)); 500df8bae1dSRodney W. Grimes } else { 501df8bae1dSRodney W. Grimes m->m_data -= optlen; 502df8bae1dSRodney W. Grimes m->m_len += optlen; 503df8bae1dSRodney W. Grimes m->m_pkthdr.len += optlen; 504df8bae1dSRodney W. Grimes ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); 505df8bae1dSRodney W. Grimes } 506df8bae1dSRodney W. Grimes ip = mtod(m, struct ip *); 50794a5d9b6SDavid Greenman (void)memcpy(ip + 1, p->ipopt_list, (unsigned)optlen); 508df8bae1dSRodney W. Grimes *phlen = sizeof(struct ip) + optlen; 5099c9137eaSGarrett Wollman ip->ip_vhl = IP_MAKE_VHL(IPVERSION, *phlen >> 2); 510df8bae1dSRodney W. Grimes ip->ip_len += optlen; 511df8bae1dSRodney W. Grimes return (m); 512df8bae1dSRodney W. Grimes } 513df8bae1dSRodney W. Grimes 514df8bae1dSRodney W. Grimes /* 515df8bae1dSRodney W. Grimes * Copy options from ip to jp, 516df8bae1dSRodney W. Grimes * omitting those not copied during fragmentation. 517df8bae1dSRodney W. Grimes */ 5180312fbe9SPoul-Henning Kamp static int 519df8bae1dSRodney W. Grimes ip_optcopy(ip, jp) 520df8bae1dSRodney W. Grimes struct ip *ip, *jp; 521df8bae1dSRodney W. Grimes { 522df8bae1dSRodney W. Grimes register u_char *cp, *dp; 523df8bae1dSRodney W. Grimes int opt, optlen, cnt; 524df8bae1dSRodney W. Grimes 525df8bae1dSRodney W. Grimes cp = (u_char *)(ip + 1); 526df8bae1dSRodney W. Grimes dp = (u_char *)(jp + 1); 5279c9137eaSGarrett Wollman cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip); 528df8bae1dSRodney W. Grimes for (; cnt > 0; cnt -= optlen, cp += optlen) { 529df8bae1dSRodney W. Grimes opt = cp[0]; 530df8bae1dSRodney W. Grimes if (opt == IPOPT_EOL) 531df8bae1dSRodney W. Grimes break; 532df8bae1dSRodney W. Grimes if (opt == IPOPT_NOP) { 533df8bae1dSRodney W. Grimes /* Preserve for IP mcast tunnel's LSRR alignment. */ 534df8bae1dSRodney W. Grimes *dp++ = IPOPT_NOP; 535df8bae1dSRodney W. Grimes optlen = 1; 536df8bae1dSRodney W. Grimes continue; 537df8bae1dSRodney W. Grimes } else 538df8bae1dSRodney W. Grimes optlen = cp[IPOPT_OLEN]; 539df8bae1dSRodney W. Grimes /* bogus lengths should have been caught by ip_dooptions */ 540df8bae1dSRodney W. Grimes if (optlen > cnt) 541df8bae1dSRodney W. Grimes optlen = cnt; 542df8bae1dSRodney W. Grimes if (IPOPT_COPIED(opt)) { 54394a5d9b6SDavid Greenman (void)memcpy(dp, cp, (unsigned)optlen); 544df8bae1dSRodney W. Grimes dp += optlen; 545df8bae1dSRodney W. Grimes } 546df8bae1dSRodney W. Grimes } 547df8bae1dSRodney W. Grimes for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) 548df8bae1dSRodney W. Grimes *dp++ = IPOPT_EOL; 549df8bae1dSRodney W. Grimes return (optlen); 550df8bae1dSRodney W. Grimes } 551df8bae1dSRodney W. Grimes 552df8bae1dSRodney W. Grimes /* 553df8bae1dSRodney W. Grimes * IP socket option processing. 554df8bae1dSRodney W. Grimes */ 555df8bae1dSRodney W. Grimes int 556df8bae1dSRodney W. Grimes ip_ctloutput(op, so, level, optname, mp) 557df8bae1dSRodney W. Grimes int op; 558df8bae1dSRodney W. Grimes struct socket *so; 559df8bae1dSRodney W. Grimes int level, optname; 560df8bae1dSRodney W. Grimes struct mbuf **mp; 561df8bae1dSRodney W. Grimes { 562df8bae1dSRodney W. Grimes register struct inpcb *inp = sotoinpcb(so); 563df8bae1dSRodney W. Grimes register struct mbuf *m = *mp; 56426f9a767SRodney W. Grimes register int optval = 0; 565df8bae1dSRodney W. Grimes int error = 0; 566df8bae1dSRodney W. Grimes 567df8bae1dSRodney W. Grimes if (level != IPPROTO_IP) { 568df8bae1dSRodney W. Grimes error = EINVAL; 569df8bae1dSRodney W. Grimes if (op == PRCO_SETOPT && *mp) 570df8bae1dSRodney W. Grimes (void) m_free(*mp); 571df8bae1dSRodney W. Grimes } else switch (op) { 572df8bae1dSRodney W. Grimes 573df8bae1dSRodney W. Grimes case PRCO_SETOPT: 574df8bae1dSRodney W. Grimes switch (optname) { 575df8bae1dSRodney W. Grimes case IP_OPTIONS: 576df8bae1dSRodney W. Grimes #ifdef notyet 577df8bae1dSRodney W. Grimes case IP_RETOPTS: 578df8bae1dSRodney W. Grimes return (ip_pcbopts(optname, &inp->inp_options, m)); 579df8bae1dSRodney W. Grimes #else 580df8bae1dSRodney W. Grimes return (ip_pcbopts(&inp->inp_options, m)); 581df8bae1dSRodney W. Grimes #endif 582df8bae1dSRodney W. Grimes 583df8bae1dSRodney W. Grimes case IP_TOS: 584df8bae1dSRodney W. Grimes case IP_TTL: 585df8bae1dSRodney W. Grimes case IP_RECVOPTS: 586df8bae1dSRodney W. Grimes case IP_RECVRETOPTS: 587df8bae1dSRodney W. Grimes case IP_RECVDSTADDR: 58840a63d93SJoerg Wunsch if (m == 0 || m->m_len != sizeof(int)) 589df8bae1dSRodney W. Grimes error = EINVAL; 590df8bae1dSRodney W. Grimes else { 591df8bae1dSRodney W. Grimes optval = *mtod(m, int *); 592df8bae1dSRodney W. Grimes switch (optname) { 593df8bae1dSRodney W. Grimes 594df8bae1dSRodney W. Grimes case IP_TOS: 595df8bae1dSRodney W. Grimes inp->inp_ip.ip_tos = optval; 596df8bae1dSRodney W. Grimes break; 597df8bae1dSRodney W. Grimes 598df8bae1dSRodney W. Grimes case IP_TTL: 599df8bae1dSRodney W. Grimes inp->inp_ip.ip_ttl = optval; 600df8bae1dSRodney W. Grimes break; 601df8bae1dSRodney W. Grimes #define OPTSET(bit) \ 602df8bae1dSRodney W. Grimes if (optval) \ 603df8bae1dSRodney W. Grimes inp->inp_flags |= bit; \ 604df8bae1dSRodney W. Grimes else \ 605df8bae1dSRodney W. Grimes inp->inp_flags &= ~bit; 606df8bae1dSRodney W. Grimes 607df8bae1dSRodney W. Grimes case IP_RECVOPTS: 608df8bae1dSRodney W. Grimes OPTSET(INP_RECVOPTS); 609df8bae1dSRodney W. Grimes break; 610df8bae1dSRodney W. Grimes 611df8bae1dSRodney W. Grimes case IP_RECVRETOPTS: 612df8bae1dSRodney W. Grimes OPTSET(INP_RECVRETOPTS); 613df8bae1dSRodney W. Grimes break; 614df8bae1dSRodney W. Grimes 615df8bae1dSRodney W. Grimes case IP_RECVDSTADDR: 616df8bae1dSRodney W. Grimes OPTSET(INP_RECVDSTADDR); 617df8bae1dSRodney W. Grimes break; 618df8bae1dSRodney W. Grimes } 619df8bae1dSRodney W. Grimes } 620df8bae1dSRodney W. Grimes break; 621df8bae1dSRodney W. Grimes #undef OPTSET 622df8bae1dSRodney W. Grimes 623df8bae1dSRodney W. Grimes case IP_MULTICAST_IF: 624f0068c4aSGarrett Wollman case IP_MULTICAST_VIF: 625df8bae1dSRodney W. Grimes case IP_MULTICAST_TTL: 626df8bae1dSRodney W. Grimes case IP_MULTICAST_LOOP: 627df8bae1dSRodney W. Grimes case IP_ADD_MEMBERSHIP: 628df8bae1dSRodney W. Grimes case IP_DROP_MEMBERSHIP: 629df8bae1dSRodney W. Grimes error = ip_setmoptions(optname, &inp->inp_moptions, m); 630df8bae1dSRodney W. Grimes break; 631df8bae1dSRodney W. Grimes 63233b3ac06SPeter Wemm case IP_PORTRANGE: 63333b3ac06SPeter Wemm if (m == 0 || m->m_len != sizeof(int)) 63433b3ac06SPeter Wemm error = EINVAL; 63533b3ac06SPeter Wemm else { 63633b3ac06SPeter Wemm optval = *mtod(m, int *); 63733b3ac06SPeter Wemm 63833b3ac06SPeter Wemm switch (optval) { 63933b3ac06SPeter Wemm 64033b3ac06SPeter Wemm case IP_PORTRANGE_DEFAULT: 64133b3ac06SPeter Wemm inp->inp_flags &= ~(INP_LOWPORT); 64233b3ac06SPeter Wemm inp->inp_flags &= ~(INP_HIGHPORT); 64333b3ac06SPeter Wemm break; 64433b3ac06SPeter Wemm 64533b3ac06SPeter Wemm case IP_PORTRANGE_HIGH: 64633b3ac06SPeter Wemm inp->inp_flags &= ~(INP_LOWPORT); 64733b3ac06SPeter Wemm inp->inp_flags |= INP_HIGHPORT; 64833b3ac06SPeter Wemm break; 64933b3ac06SPeter Wemm 65033b3ac06SPeter Wemm case IP_PORTRANGE_LOW: 65133b3ac06SPeter Wemm inp->inp_flags &= ~(INP_HIGHPORT); 65233b3ac06SPeter Wemm inp->inp_flags |= INP_LOWPORT; 65333b3ac06SPeter Wemm break; 65433b3ac06SPeter Wemm 65533b3ac06SPeter Wemm default: 65633b3ac06SPeter Wemm error = EINVAL; 65733b3ac06SPeter Wemm break; 65833b3ac06SPeter Wemm } 65933b3ac06SPeter Wemm } 660ce8c72b1SPeter Wemm break; 66133b3ac06SPeter Wemm 662df8bae1dSRodney W. Grimes default: 663df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 664df8bae1dSRodney W. Grimes break; 665df8bae1dSRodney W. Grimes } 666df8bae1dSRodney W. Grimes if (m) 667df8bae1dSRodney W. Grimes (void)m_free(m); 668df8bae1dSRodney W. Grimes break; 669df8bae1dSRodney W. Grimes 670df8bae1dSRodney W. Grimes case PRCO_GETOPT: 671df8bae1dSRodney W. Grimes switch (optname) { 672df8bae1dSRodney W. Grimes case IP_OPTIONS: 673df8bae1dSRodney W. Grimes case IP_RETOPTS: 674df8bae1dSRodney W. Grimes *mp = m = m_get(M_WAIT, MT_SOOPTS); 675df8bae1dSRodney W. Grimes if (inp->inp_options) { 676df8bae1dSRodney W. Grimes m->m_len = inp->inp_options->m_len; 67794a5d9b6SDavid Greenman (void)memcpy(mtod(m, void *), 67894a5d9b6SDavid Greenman mtod(inp->inp_options, void *), (unsigned)m->m_len); 679df8bae1dSRodney W. Grimes } else 680df8bae1dSRodney W. Grimes m->m_len = 0; 681df8bae1dSRodney W. Grimes break; 682df8bae1dSRodney W. Grimes 683df8bae1dSRodney W. Grimes case IP_TOS: 684df8bae1dSRodney W. Grimes case IP_TTL: 685df8bae1dSRodney W. Grimes case IP_RECVOPTS: 686df8bae1dSRodney W. Grimes case IP_RECVRETOPTS: 687df8bae1dSRodney W. Grimes case IP_RECVDSTADDR: 688df8bae1dSRodney W. Grimes *mp = m = m_get(M_WAIT, MT_SOOPTS); 689df8bae1dSRodney W. Grimes m->m_len = sizeof(int); 690df8bae1dSRodney W. Grimes switch (optname) { 691df8bae1dSRodney W. Grimes 692df8bae1dSRodney W. Grimes case IP_TOS: 693df8bae1dSRodney W. Grimes optval = inp->inp_ip.ip_tos; 694df8bae1dSRodney W. Grimes break; 695df8bae1dSRodney W. Grimes 696df8bae1dSRodney W. Grimes case IP_TTL: 697df8bae1dSRodney W. Grimes optval = inp->inp_ip.ip_ttl; 698df8bae1dSRodney W. Grimes break; 699df8bae1dSRodney W. Grimes 700df8bae1dSRodney W. Grimes #define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0) 701df8bae1dSRodney W. Grimes 702df8bae1dSRodney W. Grimes case IP_RECVOPTS: 703df8bae1dSRodney W. Grimes optval = OPTBIT(INP_RECVOPTS); 704df8bae1dSRodney W. Grimes break; 705df8bae1dSRodney W. Grimes 706df8bae1dSRodney W. Grimes case IP_RECVRETOPTS: 707df8bae1dSRodney W. Grimes optval = OPTBIT(INP_RECVRETOPTS); 708df8bae1dSRodney W. Grimes break; 709df8bae1dSRodney W. Grimes 710df8bae1dSRodney W. Grimes case IP_RECVDSTADDR: 711df8bae1dSRodney W. Grimes optval = OPTBIT(INP_RECVDSTADDR); 712df8bae1dSRodney W. Grimes break; 713df8bae1dSRodney W. Grimes } 714df8bae1dSRodney W. Grimes *mtod(m, int *) = optval; 715df8bae1dSRodney W. Grimes break; 716df8bae1dSRodney W. Grimes 717df8bae1dSRodney W. Grimes case IP_MULTICAST_IF: 718f0068c4aSGarrett Wollman case IP_MULTICAST_VIF: 719df8bae1dSRodney W. Grimes case IP_MULTICAST_TTL: 720df8bae1dSRodney W. Grimes case IP_MULTICAST_LOOP: 721df8bae1dSRodney W. Grimes case IP_ADD_MEMBERSHIP: 722df8bae1dSRodney W. Grimes case IP_DROP_MEMBERSHIP: 723df8bae1dSRodney W. Grimes error = ip_getmoptions(optname, inp->inp_moptions, mp); 724df8bae1dSRodney W. Grimes break; 725df8bae1dSRodney W. Grimes 72633b3ac06SPeter Wemm case IP_PORTRANGE: 72733b3ac06SPeter Wemm *mp = m = m_get(M_WAIT, MT_SOOPTS); 72833b3ac06SPeter Wemm m->m_len = sizeof(int); 72933b3ac06SPeter Wemm 73033b3ac06SPeter Wemm if (inp->inp_flags & INP_HIGHPORT) 73133b3ac06SPeter Wemm optval = IP_PORTRANGE_HIGH; 73233b3ac06SPeter Wemm else if (inp->inp_flags & INP_LOWPORT) 73333b3ac06SPeter Wemm optval = IP_PORTRANGE_LOW; 73433b3ac06SPeter Wemm else 73533b3ac06SPeter Wemm optval = 0; 73633b3ac06SPeter Wemm 73733b3ac06SPeter Wemm *mtod(m, int *) = optval; 73833b3ac06SPeter Wemm break; 73933b3ac06SPeter Wemm 740df8bae1dSRodney W. Grimes default: 741df8bae1dSRodney W. Grimes error = ENOPROTOOPT; 742df8bae1dSRodney W. Grimes break; 743df8bae1dSRodney W. Grimes } 744df8bae1dSRodney W. Grimes break; 745df8bae1dSRodney W. Grimes } 746df8bae1dSRodney W. Grimes return (error); 747df8bae1dSRodney W. Grimes } 748df8bae1dSRodney W. Grimes 749df8bae1dSRodney W. Grimes /* 750df8bae1dSRodney W. Grimes * Set up IP options in pcb for insertion in output packets. 751df8bae1dSRodney W. Grimes * Store in mbuf with pointer in pcbopt, adding pseudo-option 752df8bae1dSRodney W. Grimes * with destination address if source routed. 753df8bae1dSRodney W. Grimes */ 7540312fbe9SPoul-Henning Kamp static int 755df8bae1dSRodney W. Grimes #ifdef notyet 756df8bae1dSRodney W. Grimes ip_pcbopts(optname, pcbopt, m) 757df8bae1dSRodney W. Grimes int optname; 758df8bae1dSRodney W. Grimes #else 759df8bae1dSRodney W. Grimes ip_pcbopts(pcbopt, m) 760df8bae1dSRodney W. Grimes #endif 761df8bae1dSRodney W. Grimes struct mbuf **pcbopt; 762df8bae1dSRodney W. Grimes register struct mbuf *m; 763df8bae1dSRodney W. Grimes { 764df8bae1dSRodney W. Grimes register cnt, optlen; 765df8bae1dSRodney W. Grimes register u_char *cp; 766df8bae1dSRodney W. Grimes u_char opt; 767df8bae1dSRodney W. Grimes 768df8bae1dSRodney W. Grimes /* turn off any old options */ 769df8bae1dSRodney W. Grimes if (*pcbopt) 770df8bae1dSRodney W. Grimes (void)m_free(*pcbopt); 771df8bae1dSRodney W. Grimes *pcbopt = 0; 772df8bae1dSRodney W. Grimes if (m == (struct mbuf *)0 || m->m_len == 0) { 773df8bae1dSRodney W. Grimes /* 774df8bae1dSRodney W. Grimes * Only turning off any previous options. 775df8bae1dSRodney W. Grimes */ 776df8bae1dSRodney W. Grimes if (m) 777df8bae1dSRodney W. Grimes (void)m_free(m); 778df8bae1dSRodney W. Grimes return (0); 779df8bae1dSRodney W. Grimes } 780df8bae1dSRodney W. Grimes 781df8bae1dSRodney W. Grimes #ifndef vax 782df8bae1dSRodney W. Grimes if (m->m_len % sizeof(long)) 783df8bae1dSRodney W. Grimes goto bad; 784df8bae1dSRodney W. Grimes #endif 785df8bae1dSRodney W. Grimes /* 786df8bae1dSRodney W. Grimes * IP first-hop destination address will be stored before 787df8bae1dSRodney W. Grimes * actual options; move other options back 788df8bae1dSRodney W. Grimes * and clear it when none present. 789df8bae1dSRodney W. Grimes */ 790df8bae1dSRodney W. Grimes if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN]) 791df8bae1dSRodney W. Grimes goto bad; 792df8bae1dSRodney W. Grimes cnt = m->m_len; 793df8bae1dSRodney W. Grimes m->m_len += sizeof(struct in_addr); 794df8bae1dSRodney W. Grimes cp = mtod(m, u_char *) + sizeof(struct in_addr); 795df8bae1dSRodney W. Grimes ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt); 796df8bae1dSRodney W. Grimes bzero(mtod(m, caddr_t), sizeof(struct in_addr)); 797df8bae1dSRodney W. Grimes 798df8bae1dSRodney W. Grimes for (; cnt > 0; cnt -= optlen, cp += optlen) { 799df8bae1dSRodney W. Grimes opt = cp[IPOPT_OPTVAL]; 800df8bae1dSRodney W. Grimes if (opt == IPOPT_EOL) 801df8bae1dSRodney W. Grimes break; 802df8bae1dSRodney W. Grimes if (opt == IPOPT_NOP) 803df8bae1dSRodney W. Grimes optlen = 1; 804df8bae1dSRodney W. Grimes else { 805df8bae1dSRodney W. Grimes optlen = cp[IPOPT_OLEN]; 806df8bae1dSRodney W. Grimes if (optlen <= IPOPT_OLEN || optlen > cnt) 807df8bae1dSRodney W. Grimes goto bad; 808df8bae1dSRodney W. Grimes } 809df8bae1dSRodney W. Grimes switch (opt) { 810df8bae1dSRodney W. Grimes 811df8bae1dSRodney W. Grimes default: 812df8bae1dSRodney W. Grimes break; 813df8bae1dSRodney W. Grimes 814df8bae1dSRodney W. Grimes case IPOPT_LSRR: 815df8bae1dSRodney W. Grimes case IPOPT_SSRR: 816df8bae1dSRodney W. Grimes /* 817df8bae1dSRodney W. Grimes * user process specifies route as: 818df8bae1dSRodney W. Grimes * ->A->B->C->D 819df8bae1dSRodney W. Grimes * D must be our final destination (but we can't 820df8bae1dSRodney W. Grimes * check that since we may not have connected yet). 821df8bae1dSRodney W. Grimes * A is first hop destination, which doesn't appear in 822df8bae1dSRodney W. Grimes * actual IP option, but is stored before the options. 823df8bae1dSRodney W. Grimes */ 824df8bae1dSRodney W. Grimes if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) 825df8bae1dSRodney W. Grimes goto bad; 826df8bae1dSRodney W. Grimes m->m_len -= sizeof(struct in_addr); 827df8bae1dSRodney W. Grimes cnt -= sizeof(struct in_addr); 828df8bae1dSRodney W. Grimes optlen -= sizeof(struct in_addr); 829df8bae1dSRodney W. Grimes cp[IPOPT_OLEN] = optlen; 830df8bae1dSRodney W. Grimes /* 831df8bae1dSRodney W. Grimes * Move first hop before start of options. 832df8bae1dSRodney W. Grimes */ 833df8bae1dSRodney W. Grimes bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), 834df8bae1dSRodney W. Grimes sizeof(struct in_addr)); 835df8bae1dSRodney W. Grimes /* 836df8bae1dSRodney W. Grimes * Then copy rest of options back 837df8bae1dSRodney W. Grimes * to close up the deleted entry. 838df8bae1dSRodney W. Grimes */ 839df8bae1dSRodney W. Grimes ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] + 840df8bae1dSRodney W. Grimes sizeof(struct in_addr)), 841df8bae1dSRodney W. Grimes (caddr_t)&cp[IPOPT_OFFSET+1], 842df8bae1dSRodney W. Grimes (unsigned)cnt + sizeof(struct in_addr)); 843df8bae1dSRodney W. Grimes break; 844df8bae1dSRodney W. Grimes } 845df8bae1dSRodney W. Grimes } 846df8bae1dSRodney W. Grimes if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr)) 847df8bae1dSRodney W. Grimes goto bad; 848df8bae1dSRodney W. Grimes *pcbopt = m; 849df8bae1dSRodney W. Grimes return (0); 850df8bae1dSRodney W. Grimes 851df8bae1dSRodney W. Grimes bad: 852df8bae1dSRodney W. Grimes (void)m_free(m); 853df8bae1dSRodney W. Grimes return (EINVAL); 854df8bae1dSRodney W. Grimes } 855df8bae1dSRodney W. Grimes 856df8bae1dSRodney W. Grimes /* 857df8bae1dSRodney W. Grimes * Set the IP multicast options in response to user setsockopt(). 858df8bae1dSRodney W. Grimes */ 8590312fbe9SPoul-Henning Kamp static int 860df8bae1dSRodney W. Grimes ip_setmoptions(optname, imop, m) 861df8bae1dSRodney W. Grimes int optname; 862df8bae1dSRodney W. Grimes struct ip_moptions **imop; 863df8bae1dSRodney W. Grimes struct mbuf *m; 864df8bae1dSRodney W. Grimes { 865df8bae1dSRodney W. Grimes register int error = 0; 866df8bae1dSRodney W. Grimes u_char loop; 867df8bae1dSRodney W. Grimes register int i; 868df8bae1dSRodney W. Grimes struct in_addr addr; 869df8bae1dSRodney W. Grimes register struct ip_mreq *mreq; 870df8bae1dSRodney W. Grimes register struct ifnet *ifp; 871df8bae1dSRodney W. Grimes register struct ip_moptions *imo = *imop; 872df8bae1dSRodney W. Grimes struct route ro; 873df8bae1dSRodney W. Grimes register struct sockaddr_in *dst; 8749b626c29SGarrett Wollman int s; 875df8bae1dSRodney W. Grimes 876df8bae1dSRodney W. Grimes if (imo == NULL) { 877df8bae1dSRodney W. Grimes /* 878df8bae1dSRodney W. Grimes * No multicast option buffer attached to the pcb; 879df8bae1dSRodney W. Grimes * allocate one and initialize to default values. 880df8bae1dSRodney W. Grimes */ 881df8bae1dSRodney W. Grimes imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS, 882df8bae1dSRodney W. Grimes M_WAITOK); 883df8bae1dSRodney W. Grimes 884df8bae1dSRodney W. Grimes if (imo == NULL) 885df8bae1dSRodney W. Grimes return (ENOBUFS); 886df8bae1dSRodney W. Grimes *imop = imo; 887df8bae1dSRodney W. Grimes imo->imo_multicast_ifp = NULL; 8881c5de19aSGarrett Wollman imo->imo_multicast_vif = -1; 889df8bae1dSRodney W. Grimes imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 890df8bae1dSRodney W. Grimes imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; 891df8bae1dSRodney W. Grimes imo->imo_num_memberships = 0; 892df8bae1dSRodney W. Grimes } 893df8bae1dSRodney W. Grimes 894df8bae1dSRodney W. Grimes switch (optname) { 895f0068c4aSGarrett Wollman /* store an index number for the vif you wanna use in the send */ 896f0068c4aSGarrett Wollman case IP_MULTICAST_VIF: 8975e9ae478SGarrett Wollman if (!legal_vif_num) { 8985e9ae478SGarrett Wollman error = EOPNOTSUPP; 8995e9ae478SGarrett Wollman break; 9005e9ae478SGarrett Wollman } 901f0068c4aSGarrett Wollman if (m == NULL || m->m_len != sizeof(int)) { 902f0068c4aSGarrett Wollman error = EINVAL; 903f0068c4aSGarrett Wollman break; 904f0068c4aSGarrett Wollman } 905f0068c4aSGarrett Wollman i = *(mtod(m, int *)); 9061c5de19aSGarrett Wollman if (!legal_vif_num(i) && (i != -1)) { 907f0068c4aSGarrett Wollman error = EINVAL; 908f0068c4aSGarrett Wollman break; 909f0068c4aSGarrett Wollman } 910f0068c4aSGarrett Wollman imo->imo_multicast_vif = i; 911f0068c4aSGarrett Wollman break; 912f0068c4aSGarrett Wollman 913df8bae1dSRodney W. Grimes case IP_MULTICAST_IF: 914df8bae1dSRodney W. Grimes /* 915df8bae1dSRodney W. Grimes * Select the interface for outgoing multicast packets. 916df8bae1dSRodney W. Grimes */ 917df8bae1dSRodney W. Grimes if (m == NULL || m->m_len != sizeof(struct in_addr)) { 918df8bae1dSRodney W. Grimes error = EINVAL; 919df8bae1dSRodney W. Grimes break; 920df8bae1dSRodney W. Grimes } 921df8bae1dSRodney W. Grimes addr = *(mtod(m, struct in_addr *)); 922df8bae1dSRodney W. Grimes /* 923df8bae1dSRodney W. Grimes * INADDR_ANY is used to remove a previous selection. 924df8bae1dSRodney W. Grimes * When no interface is selected, a default one is 925df8bae1dSRodney W. Grimes * chosen every time a multicast packet is sent. 926df8bae1dSRodney W. Grimes */ 927df8bae1dSRodney W. Grimes if (addr.s_addr == INADDR_ANY) { 928df8bae1dSRodney W. Grimes imo->imo_multicast_ifp = NULL; 929df8bae1dSRodney W. Grimes break; 930df8bae1dSRodney W. Grimes } 931df8bae1dSRodney W. Grimes /* 932df8bae1dSRodney W. Grimes * The selected interface is identified by its local 933df8bae1dSRodney W. Grimes * IP address. Find the interface and confirm that 934df8bae1dSRodney W. Grimes * it supports multicasting. 935df8bae1dSRodney W. Grimes */ 93620e8807cSGarrett Wollman s = splimp(); 937df8bae1dSRodney W. Grimes INADDR_TO_IFP(addr, ifp); 938df8bae1dSRodney W. Grimes if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { 939fbc6ab00SBill Fenner splx(s); 940df8bae1dSRodney W. Grimes error = EADDRNOTAVAIL; 941df8bae1dSRodney W. Grimes break; 942df8bae1dSRodney W. Grimes } 943df8bae1dSRodney W. Grimes imo->imo_multicast_ifp = ifp; 9449b626c29SGarrett Wollman splx(s); 945df8bae1dSRodney W. Grimes break; 946df8bae1dSRodney W. Grimes 947df8bae1dSRodney W. Grimes case IP_MULTICAST_TTL: 948df8bae1dSRodney W. Grimes /* 949df8bae1dSRodney W. Grimes * Set the IP time-to-live for outgoing multicast packets. 950df8bae1dSRodney W. Grimes */ 951df8bae1dSRodney W. Grimes if (m == NULL || m->m_len != 1) { 952df8bae1dSRodney W. Grimes error = EINVAL; 953df8bae1dSRodney W. Grimes break; 954df8bae1dSRodney W. Grimes } 955df8bae1dSRodney W. Grimes imo->imo_multicast_ttl = *(mtod(m, u_char *)); 956df8bae1dSRodney W. Grimes break; 957df8bae1dSRodney W. Grimes 958df8bae1dSRodney W. Grimes case IP_MULTICAST_LOOP: 959df8bae1dSRodney W. Grimes /* 960df8bae1dSRodney W. Grimes * Set the loopback flag for outgoing multicast packets. 961df8bae1dSRodney W. Grimes * Must be zero or one. 962df8bae1dSRodney W. Grimes */ 963df8bae1dSRodney W. Grimes if (m == NULL || m->m_len != 1 || 964df8bae1dSRodney W. Grimes (loop = *(mtod(m, u_char *))) > 1) { 965df8bae1dSRodney W. Grimes error = EINVAL; 966df8bae1dSRodney W. Grimes break; 967df8bae1dSRodney W. Grimes } 968df8bae1dSRodney W. Grimes imo->imo_multicast_loop = loop; 969df8bae1dSRodney W. Grimes break; 970df8bae1dSRodney W. Grimes 971df8bae1dSRodney W. Grimes case IP_ADD_MEMBERSHIP: 972df8bae1dSRodney W. Grimes /* 973df8bae1dSRodney W. Grimes * Add a multicast group membership. 974df8bae1dSRodney W. Grimes * Group must be a valid IP multicast address. 975df8bae1dSRodney W. Grimes */ 976df8bae1dSRodney W. Grimes if (m == NULL || m->m_len != sizeof(struct ip_mreq)) { 977df8bae1dSRodney W. Grimes error = EINVAL; 978df8bae1dSRodney W. Grimes break; 979df8bae1dSRodney W. Grimes } 980df8bae1dSRodney W. Grimes mreq = mtod(m, struct ip_mreq *); 981df8bae1dSRodney W. Grimes if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) { 982df8bae1dSRodney W. Grimes error = EINVAL; 983df8bae1dSRodney W. Grimes break; 984df8bae1dSRodney W. Grimes } 98520e8807cSGarrett Wollman s = splimp(); 986df8bae1dSRodney W. Grimes /* 987df8bae1dSRodney W. Grimes * If no interface address was provided, use the interface of 988df8bae1dSRodney W. Grimes * the route to the given multicast address. 989df8bae1dSRodney W. Grimes */ 990df8bae1dSRodney W. Grimes if (mreq->imr_interface.s_addr == INADDR_ANY) { 9911c5de19aSGarrett Wollman bzero((caddr_t)&ro, sizeof(ro)); 992df8bae1dSRodney W. Grimes dst = (struct sockaddr_in *)&ro.ro_dst; 993df8bae1dSRodney W. Grimes dst->sin_len = sizeof(*dst); 994df8bae1dSRodney W. Grimes dst->sin_family = AF_INET; 995df8bae1dSRodney W. Grimes dst->sin_addr = mreq->imr_multiaddr; 996df8bae1dSRodney W. Grimes rtalloc(&ro); 997df8bae1dSRodney W. Grimes if (ro.ro_rt == NULL) { 998df8bae1dSRodney W. Grimes error = EADDRNOTAVAIL; 9999b626c29SGarrett Wollman splx(s); 1000df8bae1dSRodney W. Grimes break; 1001df8bae1dSRodney W. Grimes } 1002df8bae1dSRodney W. Grimes ifp = ro.ro_rt->rt_ifp; 1003df8bae1dSRodney W. Grimes rtfree(ro.ro_rt); 1004df8bae1dSRodney W. Grimes } 1005df8bae1dSRodney W. Grimes else { 1006df8bae1dSRodney W. Grimes INADDR_TO_IFP(mreq->imr_interface, ifp); 1007df8bae1dSRodney W. Grimes } 10089b626c29SGarrett Wollman 1009df8bae1dSRodney W. Grimes /* 1010df8bae1dSRodney W. Grimes * See if we found an interface, and confirm that it 1011df8bae1dSRodney W. Grimes * supports multicast. 1012df8bae1dSRodney W. Grimes */ 1013df8bae1dSRodney W. Grimes if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { 1014df8bae1dSRodney W. Grimes error = EADDRNOTAVAIL; 10159b626c29SGarrett Wollman splx(s); 1016df8bae1dSRodney W. Grimes break; 1017df8bae1dSRodney W. Grimes } 1018df8bae1dSRodney W. Grimes /* 1019df8bae1dSRodney W. Grimes * See if the membership already exists or if all the 1020df8bae1dSRodney W. Grimes * membership slots are full. 1021df8bae1dSRodney W. Grimes */ 1022df8bae1dSRodney W. Grimes for (i = 0; i < imo->imo_num_memberships; ++i) { 1023df8bae1dSRodney W. Grimes if (imo->imo_membership[i]->inm_ifp == ifp && 1024df8bae1dSRodney W. Grimes imo->imo_membership[i]->inm_addr.s_addr 1025df8bae1dSRodney W. Grimes == mreq->imr_multiaddr.s_addr) 1026df8bae1dSRodney W. Grimes break; 1027df8bae1dSRodney W. Grimes } 1028df8bae1dSRodney W. Grimes if (i < imo->imo_num_memberships) { 1029df8bae1dSRodney W. Grimes error = EADDRINUSE; 10309b626c29SGarrett Wollman splx(s); 1031df8bae1dSRodney W. Grimes break; 1032df8bae1dSRodney W. Grimes } 1033df8bae1dSRodney W. Grimes if (i == IP_MAX_MEMBERSHIPS) { 1034df8bae1dSRodney W. Grimes error = ETOOMANYREFS; 10359b626c29SGarrett Wollman splx(s); 1036df8bae1dSRodney W. Grimes break; 1037df8bae1dSRodney W. Grimes } 1038df8bae1dSRodney W. Grimes /* 1039df8bae1dSRodney W. Grimes * Everything looks good; add a new record to the multicast 1040df8bae1dSRodney W. Grimes * address list for the given interface. 1041df8bae1dSRodney W. Grimes */ 1042df8bae1dSRodney W. Grimes if ((imo->imo_membership[i] = 1043df8bae1dSRodney W. Grimes in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) { 1044df8bae1dSRodney W. Grimes error = ENOBUFS; 10459b626c29SGarrett Wollman splx(s); 1046df8bae1dSRodney W. Grimes break; 1047df8bae1dSRodney W. Grimes } 1048df8bae1dSRodney W. Grimes ++imo->imo_num_memberships; 10499b626c29SGarrett Wollman splx(s); 1050df8bae1dSRodney W. Grimes break; 1051df8bae1dSRodney W. Grimes 1052df8bae1dSRodney W. Grimes case IP_DROP_MEMBERSHIP: 1053df8bae1dSRodney W. Grimes /* 1054df8bae1dSRodney W. Grimes * Drop a multicast group membership. 1055df8bae1dSRodney W. Grimes * Group must be a valid IP multicast address. 1056df8bae1dSRodney W. Grimes */ 1057df8bae1dSRodney W. Grimes if (m == NULL || m->m_len != sizeof(struct ip_mreq)) { 1058df8bae1dSRodney W. Grimes error = EINVAL; 1059df8bae1dSRodney W. Grimes break; 1060df8bae1dSRodney W. Grimes } 1061df8bae1dSRodney W. Grimes mreq = mtod(m, struct ip_mreq *); 1062df8bae1dSRodney W. Grimes if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) { 1063df8bae1dSRodney W. Grimes error = EINVAL; 1064df8bae1dSRodney W. Grimes break; 1065df8bae1dSRodney W. Grimes } 10669b626c29SGarrett Wollman 106720e8807cSGarrett Wollman s = splimp(); 1068df8bae1dSRodney W. Grimes /* 1069df8bae1dSRodney W. Grimes * If an interface address was specified, get a pointer 1070df8bae1dSRodney W. Grimes * to its ifnet structure. 1071df8bae1dSRodney W. Grimes */ 1072df8bae1dSRodney W. Grimes if (mreq->imr_interface.s_addr == INADDR_ANY) 1073df8bae1dSRodney W. Grimes ifp = NULL; 1074df8bae1dSRodney W. Grimes else { 1075df8bae1dSRodney W. Grimes INADDR_TO_IFP(mreq->imr_interface, ifp); 1076df8bae1dSRodney W. Grimes if (ifp == NULL) { 1077df8bae1dSRodney W. Grimes error = EADDRNOTAVAIL; 10789b626c29SGarrett Wollman splx(s); 1079df8bae1dSRodney W. Grimes break; 1080df8bae1dSRodney W. Grimes } 1081df8bae1dSRodney W. Grimes } 1082df8bae1dSRodney W. Grimes /* 1083df8bae1dSRodney W. Grimes * Find the membership in the membership array. 1084df8bae1dSRodney W. Grimes */ 1085df8bae1dSRodney W. Grimes for (i = 0; i < imo->imo_num_memberships; ++i) { 1086df8bae1dSRodney W. Grimes if ((ifp == NULL || 1087df8bae1dSRodney W. Grimes imo->imo_membership[i]->inm_ifp == ifp) && 1088df8bae1dSRodney W. Grimes imo->imo_membership[i]->inm_addr.s_addr == 1089df8bae1dSRodney W. Grimes mreq->imr_multiaddr.s_addr) 1090df8bae1dSRodney W. Grimes break; 1091df8bae1dSRodney W. Grimes } 1092df8bae1dSRodney W. Grimes if (i == imo->imo_num_memberships) { 1093df8bae1dSRodney W. Grimes error = EADDRNOTAVAIL; 10949b626c29SGarrett Wollman splx(s); 1095df8bae1dSRodney W. Grimes break; 1096df8bae1dSRodney W. Grimes } 1097df8bae1dSRodney W. Grimes /* 1098df8bae1dSRodney W. Grimes * Give up the multicast address record to which the 1099df8bae1dSRodney W. Grimes * membership points. 1100df8bae1dSRodney W. Grimes */ 1101df8bae1dSRodney W. Grimes in_delmulti(imo->imo_membership[i]); 1102df8bae1dSRodney W. Grimes /* 1103df8bae1dSRodney W. Grimes * Remove the gap in the membership array. 1104df8bae1dSRodney W. Grimes */ 1105df8bae1dSRodney W. Grimes for (++i; i < imo->imo_num_memberships; ++i) 1106df8bae1dSRodney W. Grimes imo->imo_membership[i-1] = imo->imo_membership[i]; 1107df8bae1dSRodney W. Grimes --imo->imo_num_memberships; 11089b626c29SGarrett Wollman splx(s); 1109df8bae1dSRodney W. Grimes break; 1110df8bae1dSRodney W. Grimes 1111df8bae1dSRodney W. Grimes default: 1112df8bae1dSRodney W. Grimes error = EOPNOTSUPP; 1113df8bae1dSRodney W. Grimes break; 1114df8bae1dSRodney W. Grimes } 1115df8bae1dSRodney W. Grimes 1116df8bae1dSRodney W. Grimes /* 1117df8bae1dSRodney W. Grimes * If all options have default values, no need to keep the mbuf. 1118df8bae1dSRodney W. Grimes */ 1119df8bae1dSRodney W. Grimes if (imo->imo_multicast_ifp == NULL && 11201c5de19aSGarrett Wollman imo->imo_multicast_vif == -1 && 1121df8bae1dSRodney W. Grimes imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL && 1122df8bae1dSRodney W. Grimes imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP && 1123df8bae1dSRodney W. Grimes imo->imo_num_memberships == 0) { 1124df8bae1dSRodney W. Grimes free(*imop, M_IPMOPTS); 1125df8bae1dSRodney W. Grimes *imop = NULL; 1126df8bae1dSRodney W. Grimes } 1127df8bae1dSRodney W. Grimes 1128df8bae1dSRodney W. Grimes return (error); 1129df8bae1dSRodney W. Grimes } 1130df8bae1dSRodney W. Grimes 1131df8bae1dSRodney W. Grimes /* 1132df8bae1dSRodney W. Grimes * Return the IP multicast options in response to user getsockopt(). 1133df8bae1dSRodney W. Grimes */ 11340312fbe9SPoul-Henning Kamp static int 1135df8bae1dSRodney W. Grimes ip_getmoptions(optname, imo, mp) 1136df8bae1dSRodney W. Grimes int optname; 1137df8bae1dSRodney W. Grimes register struct ip_moptions *imo; 1138df8bae1dSRodney W. Grimes register struct mbuf **mp; 1139df8bae1dSRodney W. Grimes { 1140df8bae1dSRodney W. Grimes u_char *ttl; 1141df8bae1dSRodney W. Grimes u_char *loop; 1142df8bae1dSRodney W. Grimes struct in_addr *addr; 1143df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 1144df8bae1dSRodney W. Grimes 1145df8bae1dSRodney W. Grimes *mp = m_get(M_WAIT, MT_SOOPTS); 1146df8bae1dSRodney W. Grimes 1147df8bae1dSRodney W. Grimes switch (optname) { 1148df8bae1dSRodney W. Grimes 1149f0068c4aSGarrett Wollman case IP_MULTICAST_VIF: 1150f0068c4aSGarrett Wollman if (imo != NULL) 1151f0068c4aSGarrett Wollman *(mtod(*mp, int *)) = imo->imo_multicast_vif; 1152f0068c4aSGarrett Wollman else 11531c5de19aSGarrett Wollman *(mtod(*mp, int *)) = -1; 1154f0068c4aSGarrett Wollman (*mp)->m_len = sizeof(int); 1155f0068c4aSGarrett Wollman return(0); 1156f0068c4aSGarrett Wollman 1157df8bae1dSRodney W. Grimes case IP_MULTICAST_IF: 1158df8bae1dSRodney W. Grimes addr = mtod(*mp, struct in_addr *); 1159df8bae1dSRodney W. Grimes (*mp)->m_len = sizeof(struct in_addr); 1160df8bae1dSRodney W. Grimes if (imo == NULL || imo->imo_multicast_ifp == NULL) 1161df8bae1dSRodney W. Grimes addr->s_addr = INADDR_ANY; 1162df8bae1dSRodney W. Grimes else { 1163df8bae1dSRodney W. Grimes IFP_TO_IA(imo->imo_multicast_ifp, ia); 1164df8bae1dSRodney W. Grimes addr->s_addr = (ia == NULL) ? INADDR_ANY 1165df8bae1dSRodney W. Grimes : IA_SIN(ia)->sin_addr.s_addr; 1166df8bae1dSRodney W. Grimes } 1167df8bae1dSRodney W. Grimes return (0); 1168df8bae1dSRodney W. Grimes 1169df8bae1dSRodney W. Grimes case IP_MULTICAST_TTL: 1170df8bae1dSRodney W. Grimes ttl = mtod(*mp, u_char *); 1171df8bae1dSRodney W. Grimes (*mp)->m_len = 1; 1172df8bae1dSRodney W. Grimes *ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL 1173df8bae1dSRodney W. Grimes : imo->imo_multicast_ttl; 1174df8bae1dSRodney W. Grimes return (0); 1175df8bae1dSRodney W. Grimes 1176df8bae1dSRodney W. Grimes case IP_MULTICAST_LOOP: 1177df8bae1dSRodney W. Grimes loop = mtod(*mp, u_char *); 1178df8bae1dSRodney W. Grimes (*mp)->m_len = 1; 1179df8bae1dSRodney W. Grimes *loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP 1180df8bae1dSRodney W. Grimes : imo->imo_multicast_loop; 1181df8bae1dSRodney W. Grimes return (0); 1182df8bae1dSRodney W. Grimes 1183df8bae1dSRodney W. Grimes default: 1184df8bae1dSRodney W. Grimes return (EOPNOTSUPP); 1185df8bae1dSRodney W. Grimes } 1186df8bae1dSRodney W. Grimes } 1187df8bae1dSRodney W. Grimes 1188df8bae1dSRodney W. Grimes /* 1189df8bae1dSRodney W. Grimes * Discard the IP multicast options. 1190df8bae1dSRodney W. Grimes */ 1191df8bae1dSRodney W. Grimes void 1192df8bae1dSRodney W. Grimes ip_freemoptions(imo) 1193df8bae1dSRodney W. Grimes register struct ip_moptions *imo; 1194df8bae1dSRodney W. Grimes { 1195df8bae1dSRodney W. Grimes register int i; 1196df8bae1dSRodney W. Grimes 1197df8bae1dSRodney W. Grimes if (imo != NULL) { 1198df8bae1dSRodney W. Grimes for (i = 0; i < imo->imo_num_memberships; ++i) 1199df8bae1dSRodney W. Grimes in_delmulti(imo->imo_membership[i]); 1200df8bae1dSRodney W. Grimes free(imo, M_IPMOPTS); 1201df8bae1dSRodney W. Grimes } 1202df8bae1dSRodney W. Grimes } 1203df8bae1dSRodney W. Grimes 1204df8bae1dSRodney W. Grimes /* 1205df8bae1dSRodney W. Grimes * Routine called from ip_output() to loop back a copy of an IP multicast 1206df8bae1dSRodney W. Grimes * packet to the input queue of a specified interface. Note that this 1207df8bae1dSRodney W. Grimes * calls the output routine of the loopback "driver", but with an interface 1208f5fea3ddSPaul Traina * pointer that might NOT be a loopback interface -- evil, but easier than 1209f5fea3ddSPaul Traina * replicating that code here. 1210df8bae1dSRodney W. Grimes */ 1211df8bae1dSRodney W. Grimes static void 1212df8bae1dSRodney W. Grimes ip_mloopback(ifp, m, dst) 1213df8bae1dSRodney W. Grimes struct ifnet *ifp; 1214df8bae1dSRodney W. Grimes register struct mbuf *m; 1215df8bae1dSRodney W. Grimes register struct sockaddr_in *dst; 1216df8bae1dSRodney W. Grimes { 1217df8bae1dSRodney W. Grimes register struct ip *ip; 1218df8bae1dSRodney W. Grimes struct mbuf *copym; 1219df8bae1dSRodney W. Grimes 1220df8bae1dSRodney W. Grimes copym = m_copy(m, 0, M_COPYALL); 1221df8bae1dSRodney W. Grimes if (copym != NULL) { 1222df8bae1dSRodney W. Grimes /* 1223df8bae1dSRodney W. Grimes * We don't bother to fragment if the IP length is greater 1224df8bae1dSRodney W. Grimes * than the interface's MTU. Can this possibly matter? 1225df8bae1dSRodney W. Grimes */ 1226df8bae1dSRodney W. Grimes ip = mtod(copym, struct ip *); 1227df8bae1dSRodney W. Grimes ip->ip_len = htons((u_short)ip->ip_len); 1228df8bae1dSRodney W. Grimes ip->ip_off = htons((u_short)ip->ip_off); 1229df8bae1dSRodney W. Grimes ip->ip_sum = 0; 12309c9137eaSGarrett Wollman if (ip->ip_vhl == IP_VHL_BORING) { 12319c9137eaSGarrett Wollman ip->ip_sum = in_cksum_hdr(ip); 12329c9137eaSGarrett Wollman } else { 12339c9137eaSGarrett Wollman ip->ip_sum = in_cksum(copym, 12349c9137eaSGarrett Wollman IP_VHL_HL(ip->ip_vhl) << 2); 12359c9137eaSGarrett Wollman } 12369c9137eaSGarrett Wollman /* 12379c9137eaSGarrett Wollman * NB: 12389c9137eaSGarrett Wollman * We can't simply call ip_input() directly because 12399c9137eaSGarrett Wollman * the ip_mforward() depends on the `input interface' 12409c9137eaSGarrett Wollman * being set to something unreasonable so that we don't 12419c9137eaSGarrett Wollman * attempt to forward the looped-back copy. 12429c9137eaSGarrett Wollman * It's also not clear whether there are any lingering 12439c9137eaSGarrett Wollman * reentrancy problems in other areas which might be 12449c9137eaSGarrett Wollman * exposed by this code. For the moment, we'll err 12459c9137eaSGarrett Wollman * on the side of safety by continuing to abuse 12469c9137eaSGarrett Wollman * loinput(). 12479c9137eaSGarrett Wollman */ 12489c9137eaSGarrett Wollman #ifdef notdef 12499c9137eaSGarrett Wollman copym->m_pkthdr.rcvif = &loif[0]; 12509c9137eaSGarrett Wollman ip_input(copym) 12519c9137eaSGarrett Wollman #else 1252df8bae1dSRodney W. Grimes (void) looutput(ifp, copym, (struct sockaddr *)dst, NULL); 12539c9137eaSGarrett Wollman #endif 1254df8bae1dSRodney W. Grimes } 1255df8bae1dSRodney W. Grimes } 1256