diff options
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/if_ether.c | 8 | ||||
-rw-r--r-- | sys/netinet/if_ether.h | 100 | ||||
-rw-r--r-- | sys/netinet/igmp.c | 321 | ||||
-rw-r--r-- | sys/netinet/igmp.h | 60 | ||||
-rw-r--r-- | sys/netinet/igmp_var.h | 80 | ||||
-rw-r--r-- | sys/netinet/in.c | 132 | ||||
-rw-r--r-- | sys/netinet/in.h | 37 | ||||
-rw-r--r-- | sys/netinet/in_pcb.c | 93 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 7 | ||||
-rw-r--r-- | sys/netinet/in_pcb.old.c | 503 | ||||
-rw-r--r-- | sys/netinet/in_pcb.orig.c | 498 | ||||
-rw-r--r-- | sys/netinet/in_proto.c | 12 | ||||
-rw-r--r-- | sys/netinet/in_var.h | 117 | ||||
-rw-r--r-- | sys/netinet/ip_icmp.c | 7 | ||||
-rw-r--r-- | sys/netinet/ip_input.c | 53 | ||||
-rw-r--r-- | sys/netinet/ip_mroute.c | 1056 | ||||
-rw-r--r-- | sys/netinet/ip_mroute.h | 174 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 494 | ||||
-rw-r--r-- | sys/netinet/ip_var.h | 17 | ||||
-rw-r--r-- | sys/netinet/raw_ip.c | 54 | ||||
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 7 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 104 |
22 files changed, 3896 insertions, 38 deletions
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index ebad56b12dcfa..47efad34eaeda 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)if_ether.c 7.13 (Berkeley) 10/31/90 - * $Id: if_ether.c,v 1.4 1993/11/25 01:34:57 wollman Exp $ + * $Id: if_ether.c,v 1.5 1994/05/17 22:31:00 jkh Exp $ */ /* @@ -197,6 +197,12 @@ arpresolve(ac, m, destip, desten, usetrailers) sizeof(etherbroadcastaddr)); return (1); } +#ifdef MULTICAST + if (m->m_flags & M_MCAST) { /* multicast */ + ETHER_MAP_IP_MULTICAST(destip, desten); + return(1); + } +#endif lna = in_lnaof(*destip); /* if for us, use software loopback driver if up */ for (ia = in_ifaddr; ia; ia = ia->ia_next) diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h index 787b561e6fdf1..fdf595924c432 100644 --- a/sys/netinet/if_ether.h +++ b/sys/netinet/if_ether.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)if_ether.h 7.5 (Berkeley) 6/28/90 - * $Id: if_ether.h,v 1.4 1993/11/25 01:35:01 wollman Exp $ + * $Id: if_ether.h,v 1.5 1994/05/17 22:31:02 jkh Exp $ */ #ifndef _NETINET_IF_ETHER_H_ @@ -61,6 +61,25 @@ struct ether_header { #define ETHERMTU 1500 #define ETHERMIN (60-14) +#ifdef KERNEL +/* + * Macro to map an IP multicast address to an Ethernet multicast address. + * The high-order 25 bites of the Ethernet address are staticall assigned, + * and the low-rder 23 bites are taken from the low end of the IP address. + */ +#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \ + /* struct in_addr *ipaddr; */ \ + /* u_char enaddr[6]; */ \ +{ \ + (enaddr)[0] = 0x01; \ + (enaddr)[1] = 0x00; \ + (enaddr)[2] = 0x5e; \ + (enaddr)[3] = ((u_char *)ipaddr)[1] & 0x7f; \ + (enaddr)[4] = ((u_char *)ipaddr)[2]; \ + (enaddr)[5] = ((u_char *)ipaddr)[3]; \ +} +#endif + /* * Ethernet Address Resolution Protocol. * @@ -91,8 +110,11 @@ struct arpcom { struct ifnet ac_if; /* network-visible interface */ u_char ac_enaddr[6]; /* ethernet hardware address */ struct in_addr ac_ipaddr; /* copy of ip address- XXX */ + struct ether_multi *ac_multiaddrs; /* list of ether m'cast addrs */ + int ac_multicnt; /* length of ac_multiaddrs list */ }; + /* * Internet to ethernet address resolution table. */ @@ -106,10 +128,84 @@ struct arptab { #ifdef KERNEL extern u_char etherbroadcastaddr[6]; /* defined in net/if_ethersubr.c */ +#if defined(ISO) && !defined(MULTICAST) +#define MULTICAST 1 +#endif +#ifdef MULTICAST +u_char ether_ipmulticast_min[6]; +u_char ether_ipmulticast_max[6]; +#endif struct arptab *arptnew(); extern void ether_input(struct ifnet *, struct ether_header *, struct mbuf *); extern char *ether_sprintf(u_char *); -#endif +#ifdef MULTICAST +/* + * Ethernet multicast address structure. There is one of these for each + * multicast address or range of multicast addresses that we are supposed + * to listen to on a particular interface. They are kept in a linked list, + * rooted in the interface's arpcom structure. (This really has nothing to + * do with ARP, or with the Internet address family, but this appears to be + * the minimally-disrupting place to put it.) + */ +struct ether_multi { + u_char enm_addrlo[6]; /* low or only address of range */ + u_char enm_addrhi[6]; /* high or only address of range */ + struct arpcom *enm_ac; /* back point to arpcom */ + u_int enm_refcount; /* no. claims to this addr/range */ + struct ether_multi *enm_next; /* ptr to next ether_multi */ +} ; + +/* + * Structure used by macros below to remember position when stepping through + * all of the ether_multi records. + */ +struct ether_multistep { + struct ether_multi *e_enm; +}; + +/* + * Macro for looking up the ether_multi record for a given range of Ethernet + * multicast addresses connected to a given arpcom structure. If no matching + * record is found, "enm" returns NULL. + */ +#define ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm) \ + /* u_char addrlo[6]; */ \ + /* u_char addrhi[6]; */ \ + /* struct arpcom *ac; */ \ + /* struct ether_multi *enm; */ \ +{ \ + for ((enm) = (ac)->ac_multiaddrs; \ + (enm) != NULL && \ + (bcmp((enm)->enm_addrlo, (addrlo), 6) != 0 || \ + bcmp((enm)->enm_addrhi, (addrhi), 6) != 0); \ + (enm) = (enm)->enm_next); \ +} + +/* + * Macro to step through all of the ether_multi records, one at a time. + * The current position is remembered in "step", which the caller must + * provide. ETHER_FIRST_MULTI(), below, must be called to initialize "step" + * and get the first record. Both macros return a NULL "enm" when there + * are no remaining records. + */ +#define ETHER_NEXT_MULTI(step, enm) \ + /* struct ether_multistep step; */ \ + /* struct ether_multi *enm; */ \ +{ \ + if (((enm) = (step).e_enm) != NULL) \ + (step).e_enm = (enm)->enm_next; \ +} + +#define ETHER_FIRST_MULTI(step, ac, enm) \ + /* struct ether_multistep step; */ \ + /* struct arpcom *ac; */ \ + /* struct ether_multi *enm; */ \ +{ \ + (step).e_enm = (ac)->ac_multiaddrs; \ + ETHER_NEXT_MULTI((step), (enm)); \ +} +#endif /* _MULTICAST */ +#endif /* KERNEL */ #endif /* _NETINET_IF_ETHER_H_ */ diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c new file mode 100644 index 0000000000000..d3bdd933730ae --- /dev/null +++ b/sys/netinet/igmp.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 1988 Stephen Deering. + * Copyright (c) 1992, 1993 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)igmp.c 7.2 (Berkeley) 10/11/92 + */ + +/* Internet Group Management Protocol (IGMP) routines. */ + +#ifdef MULTICAST + +#include "param.h" +#include "mbuf.h" +#include "socket.h" +#include "protosw.h" + +#include "net/if.h" +#include "net/route.h" + +#include "in.h" +#include "in_var.h" +#include "in_systm.h" +#include "ip.h" +#include "ip_var.h" +#include "igmp.h" +#include "igmp_var.h" +#include "machine/cpufunc.h" + +extern struct ifnet loif; + +static int igmp_timers_are_running = 0; +static u_long igmp_all_hosts_group; + +static void igmp_sendreport __P((struct in_multi *)); + +void +igmp_init() +{ + /* + * To avoid byte-swapping the same value over and over again. + */ + igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); +} + +void +igmp_input(m, iphlen) + register struct mbuf *m; + register int iphlen; +{ + register struct igmp *igmp; + register struct ip *ip; + register int igmplen; + register struct ifnet *ifp = m->m_pkthdr.rcvif; + register int minlen; + register struct in_multi *inm; + register struct in_ifaddr *ia; + struct in_multistep step; + + ++igmpstat.igps_rcv_total; + + ip = mtod(m, struct ip *); + igmplen = ip->ip_len; + + /* + * Validate lengths + */ + if (igmplen < IGMP_MINLEN) { + ++igmpstat.igps_rcv_tooshort; + m_freem(m); + return; + } + minlen = iphlen + IGMP_MINLEN; + if ((m->m_flags & M_EXT || m->m_len < minlen) && + (m = m_pullup(m, minlen)) == 0) { + ++igmpstat.igps_rcv_tooshort; + return; + } + + /* + * Validate checksum + */ + m->m_data += iphlen; + m->m_len -= iphlen; + igmp = mtod(m, struct igmp *); + if (in_cksum(m, igmplen)) { + ++igmpstat.igps_rcv_badsum; + m_freem(m); + return; + } + m->m_data -= iphlen; + m->m_len += iphlen; + ip = mtod(m, struct ip *); + + switch (igmp->igmp_type) { + + case IGMP_HOST_MEMBERSHIP_QUERY: + ++igmpstat.igps_rcv_queries; + + if (ifp == &loif) + break; + + if (ip->ip_dst.s_addr != igmp_all_hosts_group) { + ++igmpstat.igps_rcv_badqueries; + m_freem(m); + return; + } + + /* + * Start the timers in all of our membership records for + * the interface on which the query arrived, except those + * that are already running and those that belong to the + * "all-hosts" group. + */ + IN_FIRST_MULTI(step, inm); + while (inm != NULL) { + if (inm->inm_ifp == ifp && inm->inm_timer == 0 && + inm->inm_addr.s_addr != igmp_all_hosts_group) { + inm->inm_timer = + IGMP_RANDOM_DELAY(inm->inm_addr); + igmp_timers_are_running = 1; + } + IN_NEXT_MULTI(step, inm); + } + + break; + + case IGMP_HOST_MEMBERSHIP_REPORT: + ++igmpstat.igps_rcv_reports; + + if (ifp == &loif) + break; + + if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || + igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { + ++igmpstat.igps_rcv_badreports; + m_freem(m); + return; + } + + /* + * KLUDGE: if the IP source address of the report has an + * unspecified (i.e., zero) subnet number, as is allowed for + * a booting host, replace it with the correct subnet number + * so that a process-level multicast routing demon can + * determine which subnet it arrived from. This is necessary + * to compensate for the lack of any way for a process to + * determine the arrival interface of an incoming packet. + */ + if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { + IFP_TO_IA(ifp, ia); + if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); + } + + /* + * If we belong to the group being reported, stop + * our timer for that group. + */ + IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); + if (inm != NULL) { + inm->inm_timer = 0; + ++igmpstat.igps_rcv_ourreports; + } + + break; + } + + /* + * Pass all valid IGMP packets up to any process(es) listening + * on a raw IGMP socket. + */ + rip_input(m); +} + +void +igmp_joingroup(inm) + struct in_multi *inm; +{ + register int s = splnet(); + + if (inm->inm_addr.s_addr == igmp_all_hosts_group || + inm->inm_ifp == &loif) + inm->inm_timer = 0; + else { + igmp_sendreport(inm); + inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr); + igmp_timers_are_running = 1; + } + splx(s); +} + +void +igmp_leavegroup(inm) + struct in_multi *inm; +{ + /* + * No action required on leaving a group. + */ +} + +void +igmp_fasttimo() +{ + register struct in_multi *inm; + register int s; + struct in_multistep step; + + /* + * Quick check to see if any work needs to be done, in order + * to minimize the overhead of fasttimo processing. + */ + if (!igmp_timers_are_running) + return; + + s = splnet(); + igmp_timers_are_running = 0; + IN_FIRST_MULTI(step, inm); + while (inm != NULL) { + if (inm->inm_timer == 0) { + /* do nothing */ + } else if (--inm->inm_timer == 0) { + igmp_sendreport(inm); + } else { + igmp_timers_are_running = 1; + } + IN_NEXT_MULTI(step, inm); + } + splx(s); +} + +static void +igmp_sendreport(inm) + register struct in_multi *inm; +{ + register struct mbuf *m; + register struct igmp *igmp; + register struct ip *ip; + register struct ip_moptions *imo; + struct ip_moptions simo; + extern struct socket *ip_mrouter; + + MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (m == NULL) + return; + /* + * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN + * is smaller than mbuf size returned by MGETHDR. + */ + m->m_data += max_linkhdr; + m->m_len = sizeof(struct ip) + IGMP_MINLEN; + m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; + + ip = mtod(m, struct ip *); + ip->ip_tos = 0; + ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; + ip->ip_off = 0; + ip->ip_p = IPPROTO_IGMP; + ip->ip_src.s_addr = INADDR_ANY; + ip->ip_dst = inm->inm_addr; + + m->m_data += sizeof(struct ip); + m->m_len -= sizeof(struct ip); + igmp = mtod(m, struct igmp *); + igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT; + igmp->igmp_code = 0; + igmp->igmp_group = inm->inm_addr; + igmp->igmp_cksum = 0; + igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); + m->m_data -= sizeof(struct ip); + m->m_len += sizeof(struct ip); + + imo = &simo; + bzero((caddr_t)imo, sizeof(*imo)); + imo->imo_multicast_ifp = inm->inm_ifp; + imo->imo_multicast_ttl = 1; + /* + * Request loopback of the report if we are acting as a multicast + * router, so that the process-level routing demon can hear it. + */ +#ifdef MROUTING + imo->imo_multicast_loop = (ip_mrouter != NULL); +#else + imo->imo_multicast_loop = 0; +#endif + + ip_output(m, NULL, NULL, IP_MULTICASTOPTS, imo); + + ++igmpstat.igps_snd_reports; +} +#endif diff --git a/sys/netinet/igmp.h b/sys/netinet/igmp.h new file mode 100644 index 0000000000000..176941914de72 --- /dev/null +++ b/sys/netinet/igmp.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1988 Stephen Deering. + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)igmp.h 7.1 (Berkeley) 7/8/92 + */ + +/* Internet Group Management Protocol (IGMP) definitions. */ + +/* + * IGMP packet format. + */ +struct igmp { + u_char igmp_type; /* version & type of IGMP message */ + u_char igmp_code; /* unused, should be zero */ + u_short igmp_cksum; /* IP-style checksum */ + struct in_addr igmp_group; /* group address being reported */ +}; /* (zero for queries) */ + +#define IGMP_MINLEN 8 + +#define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* message types, incl. version */ +#define IGMP_HOST_MEMBERSHIP_REPORT 0x12 +#define IGMP_DVMRP 0x13 /* for experimental multicast */ + /* routing protocol */ + +#define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */ + /* query (in seconds) */ diff --git a/sys/netinet/igmp_var.h b/sys/netinet/igmp_var.h new file mode 100644 index 0000000000000..01e967f99ea29 --- /dev/null +++ b/sys/netinet/igmp_var.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1988 Stephen Deering. + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)igmp_var.h 7.1 (Berkeley) 7/8/92 + */ + +/* + * Internet Group Management Protocol (IGMP), + * implementation-specific definitions. + * + * Written by Steve Deering, Stanford, May 1988. + * + * MULTICAST 1.1 + */ + +struct igmpstat { + u_int igps_rcv_total; /* total IGMP messages received */ + u_int igps_rcv_tooshort; /* received with too few bytes */ + u_int igps_rcv_badsum; /* received with bad checksum */ + u_int igps_rcv_queries; /* received membership queries */ + u_int igps_rcv_badqueries; /* received invalid queries */ + u_int igps_rcv_reports; /* received membership reports */ + u_int igps_rcv_badreports; /* received invalid reports */ + u_int igps_rcv_ourreports; /* received reports for our groups */ + u_int igps_snd_reports; /* sent membership reports */ +}; + +#ifdef KERNEL +struct igmpstat igmpstat; + +/* + * Macro to compute a random timer value between 1 and (IGMP_MAX_REPORTING_ + * DELAY * countdown frequency). We assume that the routine random() + * is defined somewhere (and that it returns a positive number). + */ +#define IGMP_RANDOM_DELAY(multiaddr) \ + /* struct in_addr multiaddr; */ \ + (random() % (IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ) + 1) + + +void igmp_init __P(()); +void igmp_input __P((struct mbuf *, int)); +void igmp_joingroup __P((struct in_multi *)); +void igmp_leavegroup __P((struct in_multi *)); +void igmp_fasttimo __P(()); +#endif + diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 2c662e2d6336e..46f423b71bbd4 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)in.c 7.17 (Berkeley) 4/20/91 - * $Id: in.c,v 1.8 1994/01/15 14:29:21 davidg Exp $ + * $Id: in.c,v 1.9 1994/05/17 22:31:03 jkh Exp $ */ #include "param.h" @@ -93,6 +93,10 @@ in_netof(in) net = i & IN_CLASSB_NET; else if (IN_CLASSC(i)) net = i & IN_CLASSC_NET; +#ifdef MULTICAST + else if (IN_CLASSD(i)) + net = i & IN_CLASSD_NET; +#endif else return (0); @@ -174,6 +178,11 @@ in_lnaof(in) } else if (IN_CLASSC(i)) { net = i & IN_CLASSC_NET; host = i & IN_CLASSC_HOST; +#ifdef MULTICAST + } else if (IN_CLASSD(i)) { + net = i & IN_CLASSD_NET; + host = i & IN_CLASSD_HOST; +#endif } else return (i); @@ -557,6 +566,18 @@ in_ifinit(ifp, ia, sin, scrub) } if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) ia->ia_flags |= IFA_ROUTE; +#ifdef MULTICAST + /* + * If the interface supports multicast, join the "all hosts" + * multicast group on that interface. + */ + if (ifp->if_flags & IFF_MULTICAST) { + struct in_addr addr; + + addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + in_addmulti(&addr, ifp); + } +#endif return (error); } @@ -603,4 +624,113 @@ in_broadcast(in) return (1); return (0); } + +#ifdef MULTICAST +/* + * Add an address to the list of IP multicast addresses for a given interface. + */ +struct in_multi * +in_addmulti(ap, ifp) + register struct in_addr *ap; + register struct ifnet *ifp; +{ + register struct in_multi *inm; + struct ifreq ifr; + struct in_ifaddr *ia; + int s = splnet(); + + /* + * See if address already in list. + */ + IN_LOOKUP_MULTI(*ap, ifp, inm); + if (inm != NULL) { + /* + * Found it; just increment the reference count. + */ + ++inm->inm_refcount; + } + else { + /* + * New address; allocate a new multicast record + * and link it into the interface's multicast list. + */ + inm = (struct in_multi *)malloc(sizeof(*inm), + M_IPMADDR, M_NOWAIT); + if (inm == NULL) { + splx(s); + return (NULL); + } + inm->inm_addr = *ap; + inm->inm_ifp = ifp; + inm->inm_refcount = 1; + IFP_TO_IA(ifp, ia); + if (ia == NULL) { + free(inm, M_IPMADDR); + splx(s); + return (NULL); + } + inm->inm_ia = ia; + inm->inm_next = ia->ia_multiaddrs; + ia->ia_multiaddrs = inm; + /* + * Ask the network driver to update its multicast reception + * filter appropriately for the new address. + */ + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = *ap; + if (ifp->if_ioctl == NULL || + (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) { + ia->ia_multiaddrs = inm->inm_next; + free(inm, M_IPMADDR); + splx(s); + return (NULL); + } + /* + * Let IGMP know that we have joined a new IP multicast group. + */ + igmp_joingroup(inm); + } + splx(s); + return (inm); +} + +/* + * Delete a multicast address record. + */ +int +in_delmulti(inm) + register struct in_multi *inm; +{ + register struct in_multi **p; + struct ifreq ifr; + int s = splnet(); + + if (--inm->inm_refcount == 0) { + /* + * No remaining claims to this record; let IGMP know that + * we are leaving the multicast group. + */ + igmp_leavegroup(inm); + /* + * Unlink from list. + */ + for (p = &inm->inm_ia->ia_multiaddrs; + *p != inm; + p = &(*p)->inm_next) + continue; + *p = (*p)->inm_next; + /* + * Notify the network driver to update its multicast reception + * filter. + */ + ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET; + ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr = + inm->inm_addr; + (*inm->inm_ifp->if_ioctl)(inm->inm_ifp, SIOCDELMULTI, + (caddr_t)&ifr); + free(inm, M_IPMADDR); + } + splx(s); +} +#endif #endif diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 48f24b39ede4f..96a9f88cea771 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)in.h 7.11 (Berkeley) 4/20/91 - * $Id: in.h,v 1.4 1993/12/19 00:52:35 wollman Exp $ + * $Id: in.h,v 1.8 1994/05/26 22:42:14 jkh Exp $ */ #ifndef _NETINET_IN_H_ @@ -47,6 +47,7 @@ */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_ICMP 1 /* control message protocol */ +#define IPPROTO_IGMP 2 /* ground control protocol */ #define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_EGP 8 /* exterior gateway protocol */ @@ -100,20 +101,37 @@ struct in_addr { #define IN_CLASSC_HOST 0x000000ffUL #define IN_CLASSD(i) (((u_long)(i) & 0xf0000000UL) == 0xe0000000UL) +#define IN_CLASSD_NET 0xf0000000UL /* These ones aren't really */ +#define IN_CLASSD_NSHIFT 28 /* net and host fields, bit */ +#define IN_CLASSD_HOST 0x0fffffffUL /* routing needn't know. */ #define IN_MULTICAST(i) IN_CLASSD(i) #define IN_EXPERIMENTAL(i) (((u_long)(i) & 0xe0000000UL) == 0xe0000000UL) #define IN_BADCLASS(i) (((u_long)(i) & 0xf0000000UL) == 0xf0000000UL) #define INADDR_ANY 0x00000000UL +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001UL +#endif #define INADDR_BROADCAST 0xffffffffUL /* must be masked */ #ifndef KERNEL #define INADDR_NONE 0xffffffffUL /* -1 return */ #endif +#define INADDR_UNSPEC_GROUP 0xe0000000UL /* 224.0.0.0 */ +#define INADDR_ALLHOSTS_GROUP 0xe0000001UL /* 244.0.0.1 */ +#define INADDR_MAX_LOCAL_GROUP 0xe00000ffUL /* 244.0.0.255 */ + #define IN_LOOPBACKNET 127 /* official! */ /* + * Define a macro to stuff the loopback address into an Internet address. + */ +#define IN_SET_LOOPBACK_ADDR(a) { \ + (a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \ + (a)->sin_family = AF_INET; } + +/* * Socket address, internet style. */ struct sockaddr_in { @@ -148,6 +166,23 @@ struct ip_opts { #define IP_RECVRETOPTS 6 /* bool; receive IP options for response */ #define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/datagram */ #define IP_RETOPTS 8 /* ip_opts; set/get IP per-packet options */ +#define IP_MULTICAST_IF 9 /* set/get IP multicast interfcae */ +#define IP_MULTICAST_TTL 10 /* set/get IP multicast timetolive */ +#define IP_MULTICAST_LOOP 11 /* set/get IP m'cast loopback */ +#define IP_ADD_MEMBERSHIP 12 /* add an IP group membership */ +#define IP_DROP_MEMBERSHIP 13 /* drop an IP group membership */ + +#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ +#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sens if a member */ +#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */ + +/* + * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. + */ +struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ; #ifdef KERNEL /* From in.c: */ diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index cc404e6f1510e..af5d742f1bac0 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91 - * $Id: in_pcb.c,v 1.5 1993/12/19 00:52:37 wollman Exp $ + * $Id: in_pcb.c,v 1.6 1994/05/17 22:31:05 jkh Exp $ */ #include "param.h" @@ -51,6 +51,9 @@ #include "ip.h" #include "in_pcb.h" #include "in_var.h" +#ifdef MULTICAST +#include "ip_var.h" +#endif int in_pcballoc(so, head) @@ -70,6 +73,53 @@ in_pcballoc(so, head) so->so_pcb = (caddr_t)inp; return (0); } +/* + * return 1 if there's a pcb whose addresses 'confict' with the + * supplied addresses. Only exact matches (address with address + * or wildcard with wildcard) are considered to be in conflict + * since in_pcblookup will resolve anything else via 'best match'. + */ +int +in_pcbconflict(head, faddr, laddr, fport, lport) + register struct inpcb *head; + register u_long faddr, laddr; + register u_short fport, lport; +{ + register struct inpcb *inp = head; + + while ((inp = inp->inp_next) != head) + if (inp->inp_lport == lport && + (fport == 0 || inp->inp_fport == fport) && + (faddr == 0 || inp->inp_faddr.s_addr == faddr) && + (laddr == 0 || inp->inp_laddr.s_addr == laddr)) + return (1); + return (0); +} + +/* + * Chose a unique (non-conflicting) local port for the inpcb list + * starting at 'head'. (A 'rover' is kept in the lport field of + * the list head to make N calls to this routine O(N^2) instead of + * O(N^3)). The port will always be + * IPPORT_RESERVED <= lport <= IPPORT_USERRESERVED + */ +u_short +in_uniqueport(head, faddr, laddr, fport) + register struct inpcb *head; + register u_long faddr, laddr; + register u_short fport; +{ + register u_short lport = head->inp_lport; + + do { + ++lport; + if (lport < IPPORT_RESERVED || lport > IPPORT_USERRESERVED) + lport = IPPORT_RESERVED; + } while (in_pcbconflict(head, faddr, laddr, fport, htons(lport))); + head->inp_lport = lport; + return (htons(lport)); +} + int in_pcbbind(inp, nam) @@ -107,6 +157,7 @@ in_pcbbind(inp, nam) if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0) return (EACCES); /* even GROSSER, but this is the Internet */ +#ifdef notdef if ((so->so_options & SO_REUSEADDR) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) @@ -114,10 +165,16 @@ in_pcbbind(inp, nam) if (in_pcblookup(head, zeroin_addr, 0, sin->sin_addr, lport, wild)) return (EADDRINUSE); +#else + if ((inp->inp_socket->so_options & SO_REUSEADDR) == 0 && + in_pcbconflict(head, 0, sin->sin_addr.s_addr, 0, lport)) + return (EADDRINUSE); +#endif } inp->inp_laddr = sin->sin_addr; noname: if (lport == 0) +#ifdef notdef do { if (head->inp_lport++ < IPPORT_RESERVED || head->inp_lport > IPPORT_USERRESERVED) @@ -125,6 +182,10 @@ noname: lport = htons(head->inp_lport); } while (in_pcblookup(head, zeroin_addr, 0, inp->inp_laddr, lport, 0)); +#else + lport = in_uniqueport(head, inp->inp_faddr.s_addr, + inp->inp_laddr, inp->inp_fport); +#endif inp->inp_lport = lport; return (0); } @@ -148,8 +209,13 @@ in_pcbconnect(inp, nam) return (EINVAL); if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); +#ifdef MULTICAST + if (sin->sin_port == 0 && !IN_MULTICAST(sin->sin_addr.s_addr)) + return(EADDRNOTAVAIL); +#else if (sin->sin_port == 0) return (EADDRNOTAVAIL); +#endif if (in_ifaddr) { /* * If the destination address is INADDR_ANY, @@ -217,6 +283,28 @@ in_pcbconnect(inp, nam) if (ia == 0) return (EADDRNOTAVAIL); } +#ifdef MULTICAST + /* + * If the destination address is multicast and an outgoing + * interface has been set as a multicast option, use the + * address of that interface as our source address. + */ + if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && + inp->inp_moptions != NULL) { + struct ip_moptions *imo; + struct ifnet *ifp; + + imo = inp->inp_moptions; + if (imo->imo_multicast_ifp != NULL) { + ifp = imo->imo_multicast_ifp; + for (ia = in_ifaddr; ia; ia = ia->ia_next) + if (ia->ia_ifp == ifp) + break; + if (ia == 0) + return (EADDRNOTAVAIL); + } + } +#endif ifaddr = (struct sockaddr_in *)&ia->ia_addr; } if (in_pcblookup(inp->inp_head, @@ -269,6 +357,9 @@ in_pcbdetach(inp) (void)m_free(inp->inp_options); if (inp->inp_route.ro_rt) rtfree(inp->inp_route.ro_rt); +#ifdef MULTICAST + ip_freemoptions(inp->inp_moptions); +#endif remque(inp); (void) m_free(dtom(inp)); } diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index e4adb614e6d40..dd4527e768d13 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 1982, 1986, 1990 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1982, 1986, 1990, 1993 + * Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)in_pcb.h 7.6 (Berkeley) 6/28/90 - * $Id: in_pcb.h,v 1.5 1993/11/25 01:35:06 wollman Exp $ + * $Id: in_pcb.h,v 1.6 1994/05/17 22:31:06 jkh Exp $ */ #ifndef _NETINET_IN_PCB_H_ @@ -67,6 +67,7 @@ struct inpcb { /* function to call when MTU may have * changed */ #endif /* MTUDISC */ + struct ip_moptions *inp_moptions; /* IP multicast options */ }; /* flags in inp_flags: */ diff --git a/sys/netinet/in_pcb.old.c b/sys/netinet/in_pcb.old.c new file mode 100644 index 0000000000000..1946b9edb34a4 --- /dev/null +++ b/sys/netinet/in_pcb.old.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 1982, 1986, 1991 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91 + * $Id: in_pcb.old.c,v 1.1 1994/05/17 23:16:40 jkh Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "malloc.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" +#include "ioctl.h" + +#include "../net/if.h" +#include "../net/route.h" + +#include "in.h" +#include "in_systm.h" +#include "ip.h" +#include "in_pcb.h" +#include "in_var.h" +#ifdef MULTICAST +#include "ip_var.h" +#endif + +int +in_pcballoc(so, head) + struct socket *so; + struct inpcb *head; +{ + struct mbuf *m; + register struct inpcb *inp; + + m = m_getclr(M_DONTWAIT, MT_PCB); + if (m == NULL) + return (ENOBUFS); + inp = mtod(m, struct inpcb *); + inp->inp_head = head; + inp->inp_socket = so; + insque(inp, head); + so->so_pcb = (caddr_t)inp; + return (0); +} + +int +in_pcbbind(inp, nam) + register struct inpcb *inp; + struct mbuf *nam; +{ + register struct socket *so = inp->inp_socket; + register struct inpcb *head = inp->inp_head; + register struct sockaddr_in *sin; + u_short lport = 0; + + if (in_ifaddr == 0) + return (EADDRNOTAVAIL); + if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) + return (EINVAL); + if (nam == 0) + goto noname; + sin = mtod(nam, struct sockaddr_in *); + if (nam->m_len != sizeof (*sin)) + return (EINVAL); + if (sin->sin_addr.s_addr != INADDR_ANY) { + int tport = sin->sin_port; + + sin->sin_port = 0; /* yech... */ + if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) + return (EADDRNOTAVAIL); + sin->sin_port = tport; + } + lport = sin->sin_port; + if (lport) { + u_short aport = ntohs(lport); + int wild = 0; + + /* GROSS */ + if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0) + return (EACCES); + /* even GROSSER, but this is the Internet */ + if ((so->so_options & SO_REUSEADDR) == 0 && + ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || + (so->so_options & SO_ACCEPTCONN) == 0)) + wild = INPLOOKUP_WILDCARD; + if (in_pcblookup(head, + zeroin_addr, 0, sin->sin_addr, lport, wild)) + return (EADDRINUSE); + } + inp->inp_laddr = sin->sin_addr; +noname: + if (lport == 0) + do { + if (head->inp_lport++ < IPPORT_RESERVED || + head->inp_lport > IPPORT_USERRESERVED) + head->inp_lport = IPPORT_RESERVED; + lport = htons(head->inp_lport); + } while (in_pcblookup(head, + zeroin_addr, 0, inp->inp_laddr, lport, 0)); + inp->inp_lport = lport; + return (0); +} + +/* + * Connect from a socket to a specified address. + * Both address and port must be specified in argument sin. + * If don't have a local address for this socket yet, + * then pick one. + */ +int +in_pcbconnect(inp, nam) + register struct inpcb *inp; + struct mbuf *nam; +{ + struct in_ifaddr *ia; + struct sockaddr_in *ifaddr = 0; + register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); + + if (nam->m_len != sizeof (*sin)) + return (EINVAL); + if (sin->sin_family != AF_INET) + return (EAFNOSUPPORT); +#ifdef MULTICAST + if (sin->sin_port == 0 && !IN_MULTICAST(sin->sin_addr.s_addr)) + return (EADDRNOTAVAIL); +#else + if (sin->sin_port == 0) + return (EADDRNOTAVAIL); +#endif + if (in_ifaddr) { + /* + * If the destination address is INADDR_ANY, + * use the primary local address. + * If the supplied address is INADDR_BROADCAST, + * and the primary interface supports broadcast, + * choose the broadcast address for that interface. + */ +#define satosin(sa) ((struct sockaddr_in *)(sa)) + if (sin->sin_addr.s_addr == INADDR_ANY) + sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr; + else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && + (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST)) + sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr; + } + if (inp->inp_laddr.s_addr == INADDR_ANY) { + register struct route *ro; + struct ifnet *ifp; + + ia = (struct in_ifaddr *)0; + /* + * If route is known or can be allocated now, + * our src addr is taken from the i/f, else punt. + */ + ro = &inp->inp_route; + if (ro->ro_rt && + (satosin(&ro->ro_dst)->sin_addr.s_addr != + sin->sin_addr.s_addr || + inp->inp_socket->so_options & SO_DONTROUTE)) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)0; + } + if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ + (ro->ro_rt == (struct rtentry *)0 || + ro->ro_rt->rt_ifp == (struct ifnet *)0)) { + /* No route yet, so try to acquire one */ + ro->ro_dst.sa_family = AF_INET; + ro->ro_dst.sa_len = sizeof(struct sockaddr_in); + ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = + sin->sin_addr; + rtalloc(ro); + } + /* + * If we found a route, use the address + * corresponding to the outgoing interface + * unless it is the loopback (in case a route + * to our address on another net goes to loopback). + */ + if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) && + (ifp->if_flags & IFF_LOOPBACK) == 0) + for (ia = in_ifaddr; ia; ia = ia->ia_next) + if (ia->ia_ifp == ifp) + break; + if (ia == 0) { + int fport = sin->sin_port; + + sin->sin_port = 0; + ia = (struct in_ifaddr *) + ifa_ifwithdstaddr((struct sockaddr *)sin); + sin->sin_port = fport; + if (ia == 0) + ia = in_iaonnetof(in_netof(sin->sin_addr)); + if (ia == 0) + ia = in_ifaddr; + if (ia == 0) + return (EADDRNOTAVAIL); + } +#ifdef MULTICAST + /* + * If the destination address is multicast and an outgoing + * interface has been set as a multicast option, use the + * address of that interface as our source address. + */ + if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && + inp->inp_moptions != NULL) { + struct ip_moptions *imo; + struct ifnet *ifp; + + imo = inp->inp_moptions; + if (imo->imo_multicast_ifp != NULL) { + ifp = imo->imo_multicast_ifp; + for (ia = in_ifaddr; ia; ia = ia->ia_next) + if (ia->ia_ifp == ifp) + break; + if (ia == 0) + return (EADDRNOTAVAIL); + } + } +#endif + ifaddr = (struct sockaddr_in *)&ia->ia_addr; + } + if (in_pcblookup(inp->inp_head, + sin->sin_addr, + sin->sin_port, + inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, + inp->inp_lport, + 0)) + return (EADDRINUSE); + if (inp->inp_laddr.s_addr == INADDR_ANY) { + if (inp->inp_lport == 0) + (void)in_pcbbind(inp, (struct mbuf *)0); + inp->inp_laddr = ifaddr->sin_addr; + } + inp->inp_faddr = sin->sin_addr; + inp->inp_fport = sin->sin_port; +#ifdef MTUDISC + /* + * If the upper layer asked for PMTU discovery services, see + * if we can get an idea of what the MTU should be... + */ + in_pcbmtu(inp); +#endif /* MTUDISC */ + return (0); +} + +void +in_pcbdisconnect(inp) + struct inpcb *inp; +{ + + inp->inp_faddr.s_addr = INADDR_ANY; + inp->inp_fport = 0; +#ifdef MTUDISC + inp->inp_flags &= ~INP_MTUDISCOVERED; +#endif + if (inp->inp_socket->so_state & SS_NOFDREF) + in_pcbdetach(inp); +} + +void +in_pcbdetach(inp) + struct inpcb *inp; +{ + struct socket *so = inp->inp_socket; + + so->so_pcb = 0; + sofree(so); + if (inp->inp_options) + (void)m_free(inp->inp_options); + if (inp->inp_route.ro_rt) + rtfree(inp->inp_route.ro_rt); +#ifdef MULTICAST + ip_freemoptions(inp->inp_moptions); +#endif + remque(inp); + (void) m_free(dtom(inp)); +} + +void +in_setsockaddr(inp, nam) + register struct inpcb *inp; + struct mbuf *nam; +{ + register struct sockaddr_in *sin; + + nam->m_len = sizeof (*sin); + sin = mtod(nam, struct sockaddr_in *); + bzero((caddr_t)sin, sizeof (*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_port = inp->inp_lport; + sin->sin_addr = inp->inp_laddr; +} + +void +in_setpeeraddr(inp, nam) + struct inpcb *inp; + struct mbuf *nam; +{ + register struct sockaddr_in *sin; + + nam->m_len = sizeof (*sin); + sin = mtod(nam, struct sockaddr_in *); + bzero((caddr_t)sin, sizeof (*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_port = inp->inp_fport; + sin->sin_addr = inp->inp_faddr; +} + +/* + * Pass some notification to all connections of a protocol + * associated with address dst. The local address and/or port numbers + * may be specified to limit the search. The "usual action" will be + * taken, depending on the ctlinput cmd. The caller must filter any + * cmds that are uninteresting (e.g., no error in the map). + * Call the protocol specific routine (if any) to report + * any errors for each matching socket. + * + * Must be called at splnet. + */ +void +in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify) + struct inpcb *head; + struct sockaddr *dst; + u_short fport, lport; + struct in_addr laddr; + int cmd; + void (*notify)(struct inpcb *, int); +{ + register struct inpcb *inp, *oinp; + struct in_addr faddr; + int errno; + + if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) + return; + faddr = ((struct sockaddr_in *)dst)->sin_addr; + if (faddr.s_addr == INADDR_ANY) + return; + + /* + * Redirects go to all references to the destination, + * and use in_rtchange to invalidate the route cache. + * Dead host indications: notify all references to the destination. + * MTU change indications: same thing. + * Otherwise, if we have knowledge of the local port and address, + * deliver only to that socket. + */ + if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD + || cmd == PRC_MTUCHANGED) { + fport = 0; + lport = 0; + laddr.s_addr = 0; + if (cmd != PRC_HOSTDEAD && cmd != PRC_MTUCHANGED) + notify = in_rtchange; + } + errno = inetctlerrmap[cmd]; + for (inp = head->inp_next; inp != head;) { + if (inp->inp_faddr.s_addr != faddr.s_addr || + inp->inp_socket == 0 || + (lport && inp->inp_lport != lport) || + (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || + (fport && inp->inp_fport != fport)) { + inp = inp->inp_next; + continue; + } + oinp = inp; + inp = inp->inp_next; + if (notify) + (*notify)(oinp, errno); + } +} + +/* + * Check for alternatives when higher level complains + * about service problems. For now, invalidate cached + * routing information. If the route was created dynamically + * (by a redirect), time to try a default gateway again. + */ +void +in_losing(inp) + struct inpcb *inp; +{ + register struct rtentry *rt; + + if ((rt = inp->inp_route.ro_rt)) { + rt_missmsg(RTM_LOSING, &inp->inp_route.ro_dst, + rt->rt_gateway, (struct sockaddr *)rt_mask(rt), + (struct sockaddr *)0, rt->rt_flags, 0); + if (rt->rt_flags & RTF_DYNAMIC) + (void) rtrequest(RTM_DELETE, rt_key(rt), + rt->rt_gateway, rt_mask(rt), rt->rt_flags, + (struct rtentry **)0); + inp->inp_route.ro_rt = 0; + rtfree(rt); + +#ifdef MTUDISC + /* + * When doing MTU discovery, we want to find out as + * quickly as possible what the MTU of the new route is. + */ + in_pcbmtu(inp); +#endif /* MTUDISC */ + } +} + +/* + * After a routing change, flush old routing + * and allocate a (hopefully) better one. + */ +void +in_rtchange(inp, errno) + register struct inpcb *inp; + int errno; +{ + if (inp->inp_route.ro_rt) { + rtfree(inp->inp_route.ro_rt); + inp->inp_route.ro_rt = 0; +#ifdef MTUDISC + /* + * A new route can be allocated the next time + * output is attempted, but make sure to let + * MTU discovery know about it. + */ + in_pcbmtu(inp); +#endif /* MTUDISC */ + } +} + +struct inpcb * +in_pcblookup(head, faddr, fport, laddr, lport, flags) + struct inpcb *head; + struct in_addr faddr, laddr; + u_short fport, lport; + int flags; +{ + register struct inpcb *inp, *match = 0; + int matchwild = 3, wildcard; + + for (inp = head->inp_next; inp != head; inp = inp->inp_next) { + if (inp->inp_lport != lport) + continue; + wildcard = 0; + if (inp->inp_laddr.s_addr != INADDR_ANY) { + if (laddr.s_addr == INADDR_ANY) + wildcard++; + else if (inp->inp_laddr.s_addr != laddr.s_addr) + continue; + } else { + if (laddr.s_addr != INADDR_ANY) + wildcard++; + } + if (inp->inp_faddr.s_addr != INADDR_ANY) { + if (faddr.s_addr == INADDR_ANY) + wildcard++; + else if (inp->inp_faddr.s_addr != faddr.s_addr || + inp->inp_fport != fport) + continue; + } else { + if (faddr.s_addr != INADDR_ANY) + wildcard++; + } + if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) + continue; + if (wildcard < matchwild) { + match = inp; + matchwild = wildcard; + if (matchwild == 0) + break; + } + } + return (match); +} diff --git a/sys/netinet/in_pcb.orig.c b/sys/netinet/in_pcb.orig.c new file mode 100644 index 0000000000000..badedf559fda6 --- /dev/null +++ b/sys/netinet/in_pcb.orig.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 1982, 1986, 1991 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)in_pcb.c 7.14 (Berkeley) 4/20/91 + * $Id: in_pcb.orig.c,v 1.1 1994/05/17 23:16:41 jkh Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "malloc.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" +#include "ioctl.h" + +#include "../net/if.h" +#include "../net/route.h" + +#include "in.h" +#include "in_systm.h" +#include "ip.h" +#include "in_pcb.h" +#include "in_var.h" +#ifdef MULTICAST +#include "ip_var.h" +#endif + +int +in_pcballoc(so, head) + struct socket *so; + struct inpcb *head; +{ + struct mbuf *m; + register struct inpcb *inp; + + m = m_getclr(M_DONTWAIT, MT_PCB); + if (m == NULL) + return (ENOBUFS); + inp = mtod(m, struct inpcb *); + inp->inp_head = head; + inp->inp_socket = so; + insque(inp, head); + so->so_pcb = (caddr_t)inp; + return (0); +} + +int +in_pcbbind(inp, nam) + register struct inpcb *inp; + struct mbuf *nam; +{ + register struct socket *so = inp->inp_socket; + register struct inpcb *head = inp->inp_head; + register struct sockaddr_in *sin; + u_short lport = 0; + + if (in_ifaddr == 0) + return (EADDRNOTAVAIL); + if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) + return (EINVAL); + if (nam == 0) + goto noname; + sin = mtod(nam, struct sockaddr_in *); + if (nam->m_len != sizeof (*sin)) + return (EINVAL); + if (sin->sin_addr.s_addr != INADDR_ANY) { + int tport = sin->sin_port; + + sin->sin_port = 0; /* yech... */ + if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) + return (EADDRNOTAVAIL); + sin->sin_port = tport; + } + lport = sin->sin_port; + if (lport) { + u_short aport = ntohs(lport); + int wild = 0; + + /* GROSS */ + if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0) + return (EACCES); + /* even GROSSER, but this is the Internet */ + if ((so->so_options & SO_REUSEADDR) == 0 && + ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || + (so->so_options & SO_ACCEPTCONN) == 0)) + wild = INPLOOKUP_WILDCARD; + if (in_pcblookup(head, + zeroin_addr, 0, sin->sin_addr, lport, wild)) + return (EADDRINUSE); + } + inp->inp_laddr = sin->sin_addr; +noname: + if (lport == 0) + do { + if (head->inp_lport++ < IPPORT_RESERVED || + head->inp_lport > IPPORT_USERRESERVED) + head->inp_lport = IPPORT_RESERVED; + lport = htons(head->inp_lport); + } while (in_pcblookup(head, + zeroin_addr, 0, inp->inp_laddr, lport, 0)); + inp->inp_lport = lport; + return (0); +} + +/* + * Connect from a socket to a specified address. + * Both address and port must be specified in argument sin. + * If don't have a local address for this socket yet, + * then pick one. + */ +int +in_pcbconnect(inp, nam) + register struct inpcb *inp; + struct mbuf *nam; +{ + struct in_ifaddr *ia; + struct sockaddr_in *ifaddr = 0; + register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); + + if (nam->m_len != sizeof (*sin)) + return (EINVAL); + if (sin->sin_family != AF_INET) + return (EAFNOSUPPORT); + if (sin->sin_port == 0) + return (EADDRNOTAVAIL); + if (in_ifaddr) { + /* + * If the destination address is INADDR_ANY, + * use the primary local address. + * If the supplied address is INADDR_BROADCAST, + * and the primary interface supports broadcast, + * choose the broadcast address for that interface. + */ +#define satosin(sa) ((struct sockaddr_in *)(sa)) + if (sin->sin_addr.s_addr == INADDR_ANY) + sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr; + else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && + (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST)) + sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr; + } + if (inp->inp_laddr.s_addr == INADDR_ANY) { + register struct route *ro; + struct ifnet *ifp; + + ia = (struct in_ifaddr *)0; + /* + * If route is known or can be allocated now, + * our src addr is taken from the i/f, else punt. + */ + ro = &inp->inp_route; + if (ro->ro_rt && + (satosin(&ro->ro_dst)->sin_addr.s_addr != + sin->sin_addr.s_addr || + inp->inp_socket->so_options & SO_DONTROUTE)) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)0; + } + if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ + (ro->ro_rt == (struct rtentry *)0 || + ro->ro_rt->rt_ifp == (struct ifnet *)0)) { + /* No route yet, so try to acquire one */ + ro->ro_dst.sa_family = AF_INET; + ro->ro_dst.sa_len = sizeof(struct sockaddr_in); + ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = + sin->sin_addr; + rtalloc(ro); + } + /* + * If we found a route, use the address + * corresponding to the outgoing interface + * unless it is the loopback (in case a route + * to our address on another net goes to loopback). + */ + if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) && + (ifp->if_flags & IFF_LOOPBACK) == 0) + for (ia = in_ifaddr; ia; ia = ia->ia_next) + if (ia->ia_ifp == ifp) + break; + if (ia == 0) { + int fport = sin->sin_port; + + sin->sin_port = 0; + ia = (struct in_ifaddr *) + ifa_ifwithdstaddr((struct sockaddr *)sin); + sin->sin_port = fport; + if (ia == 0) + ia = in_iaonnetof(in_netof(sin->sin_addr)); + if (ia == 0) + ia = in_ifaddr; + if (ia == 0) + return (EADDRNOTAVAIL); + } +#ifdef MULTICAST + /* + * If the destination address is multicast and an outgoing + * interface has been set as a multicast option, use the + * address of that interface as our source address. + */ + if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && + inp->inp_moptions != NULL) { + struct ip_moptions *imo; + struct ifnet *ifp; + + imo = inp->inp_moptions; + if (imo->imo_multicast_ifp != NULL) { + ifp = imo->imo_multicast_ifp; + for (ia = in_ifaddr; ia; ia = ia->ia_next) + if (ia->ia_ifp == ifp) + break; + if (ia == 0) + return (EADDRNOTAVAIL); + } + } +#endif + ifaddr = (struct sockaddr_in *)&ia->ia_addr; + } + if (in_pcblookup(inp->inp_head, + sin->sin_addr, + sin->sin_port, + inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, + inp->inp_lport, + 0)) + return (EADDRINUSE); + if (inp->inp_laddr.s_addr == INADDR_ANY) { + if (inp->inp_lport == 0) + (void)in_pcbbind(inp, (struct mbuf *)0); + inp->inp_laddr = ifaddr->sin_addr; + } + inp->inp_faddr = sin->sin_addr; + inp->inp_fport = sin->sin_port; +#ifdef MTUDISC + /* + * If the upper layer asked for PMTU discovery services, see + * if we can get an idea of what the MTU should be... + */ + in_pcbmtu(inp); +#endif /* MTUDISC */ + return (0); +} + +void +in_pcbdisconnect(inp) + struct inpcb *inp; +{ + + inp->inp_faddr.s_addr = INADDR_ANY; + inp->inp_fport = 0; +#ifdef MTUDISC + inp->inp_flags &= ~INP_MTUDISCOVERED; +#endif + if (inp->inp_socket->so_state & SS_NOFDREF) + in_pcbdetach(inp); +} + +void +in_pcbdetach(inp) + struct inpcb *inp; +{ + struct socket *so = inp->inp_socket; + + so->so_pcb = 0; + sofree(so); + if (inp->inp_options) + (void)m_free(inp->inp_options); + if (inp->inp_route.ro_rt) + rtfree(inp->inp_route.ro_rt); +#ifdef MULTICAST + ip_freemoptions(inp->inp_moptions); +#endif + remque(inp); + (void) m_free(dtom(inp)); +} + +void +in_setsockaddr(inp, nam) + register struct inpcb *inp; + struct mbuf *nam; +{ + register struct sockaddr_in *sin; + + nam->m_len = sizeof (*sin); + sin = mtod(nam, struct sockaddr_in *); + bzero((caddr_t)sin, sizeof (*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_port = inp->inp_lport; + sin->sin_addr = inp->inp_laddr; +} + +void +in_setpeeraddr(inp, nam) + struct inpcb *inp; + struct mbuf *nam; +{ + register struct sockaddr_in *sin; + + nam->m_len = sizeof (*sin); + sin = mtod(nam, struct sockaddr_in *); + bzero((caddr_t)sin, sizeof (*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_port = inp->inp_fport; + sin->sin_addr = inp->inp_faddr; +} + +/* + * Pass some notification to all connections of a protocol + * associated with address dst. The local address and/or port numbers + * may be specified to limit the search. The "usual action" will be + * taken, depending on the ctlinput cmd. The caller must filter any + * cmds that are uninteresting (e.g., no error in the map). + * Call the protocol specific routine (if any) to report + * any errors for each matching socket. + * + * Must be called at splnet. + */ +void +in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify) + struct inpcb *head; + struct sockaddr *dst; + u_short fport, lport; + struct in_addr laddr; + int cmd; + void (*notify)(struct inpcb *, int); +{ + register struct inpcb *inp, *oinp; + struct in_addr faddr; + int errno; + + if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) + return; + faddr = ((struct sockaddr_in *)dst)->sin_addr; + if (faddr.s_addr == INADDR_ANY) + return; + + /* + * Redirects go to all references to the destination, + * and use in_rtchange to invalidate the route cache. + * Dead host indications: notify all references to the destination. + * MTU change indications: same thing. + * Otherwise, if we have knowledge of the local port and address, + * deliver only to that socket. + */ + if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD + || cmd == PRC_MTUCHANGED) { + fport = 0; + lport = 0; + laddr.s_addr = 0; + if (cmd != PRC_HOSTDEAD && cmd != PRC_MTUCHANGED) + notify = in_rtchange; + } + errno = inetctlerrmap[cmd]; + for (inp = head->inp_next; inp != head;) { + if (inp->inp_faddr.s_addr != faddr.s_addr || + inp->inp_socket == 0 || + (lport && inp->inp_lport != lport) || + (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || + (fport && inp->inp_fport != fport)) { + inp = inp->inp_next; + continue; + } + oinp = inp; + inp = inp->inp_next; + if (notify) + (*notify)(oinp, errno); + } +} + +/* + * Check for alternatives when higher level complains + * about service problems. For now, invalidate cached + * routing information. If the route was created dynamically + * (by a redirect), time to try a default gateway again. + */ +void +in_losing(inp) + struct inpcb *inp; +{ + register struct rtentry *rt; + + if ((rt = inp->inp_route.ro_rt)) { + rt_missmsg(RTM_LOSING, &inp->inp_route.ro_dst, + rt->rt_gateway, (struct sockaddr *)rt_mask(rt), + (struct sockaddr *)0, rt->rt_flags, 0); + if (rt->rt_flags & RTF_DYNAMIC) + (void) rtrequest(RTM_DELETE, rt_key(rt), + rt->rt_gateway, rt_mask(rt), rt->rt_flags, + (struct rtentry **)0); + inp->inp_route.ro_rt = 0; + rtfree(rt); + +#ifdef MTUDISC + /* + * When doing MTU discovery, we want to find out as + * quickly as possible what the MTU of the new route is. + */ + in_pcbmtu(inp); +#endif /* MTUDISC */ + } +} + +/* + * After a routing change, flush old routing + * and allocate a (hopefully) better one. + */ +void +in_rtchange(inp, errno) + register struct inpcb *inp; + int errno; +{ + if (inp->inp_route.ro_rt) { + rtfree(inp->inp_route.ro_rt); + inp->inp_route.ro_rt = 0; +#ifdef MTUDISC + /* + * A new route can be allocated the next time + * output is attempted, but make sure to let + * MTU discovery know about it. + */ + in_pcbmtu(inp); +#endif /* MTUDISC */ + } +} + +struct inpcb * +in_pcblookup(head, faddr, fport, laddr, lport, flags) + struct inpcb *head; + struct in_addr faddr, laddr; + u_short fport, lport; + int flags; +{ + register struct inpcb *inp, *match = 0; + int matchwild = 3, wildcard; + + for (inp = head->inp_next; inp != head; inp = inp->inp_next) { + if (inp->inp_lport != lport) + continue; + wildcard = 0; + if (inp->inp_laddr.s_addr != INADDR_ANY) { + if (laddr.s_addr == INADDR_ANY) + wildcard++; + else if (inp->inp_laddr.s_addr != laddr.s_addr) + continue; + } else { + if (laddr.s_addr != INADDR_ANY) + wildcard++; + } + if (inp->inp_faddr.s_addr != INADDR_ANY) { + if (faddr.s_addr == INADDR_ANY) + wildcard++; + else if (inp->inp_faddr.s_addr != faddr.s_addr || + inp->inp_fport != fport) + continue; + } else { + if (faddr.s_addr != INADDR_ANY) + wildcard++; + } + if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0) + continue; + if (wildcard < matchwild) { + match = inp; + matchwild = wildcard; + if (matchwild == 0) + break; + } + } + return (match); +} diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index dc147f090b51b..c94cdd0a101b9 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)in_proto.c 7.5 (Berkeley) 6/28/90 - * $Id: in_proto.c,v 1.3 1993/12/19 00:52:38 wollman Exp $ + * $Id: in_proto.c,v 1.4 1994/05/17 22:31:07 jkh Exp $ */ #include "param.h" @@ -55,6 +55,9 @@ #include "udp.h" #include "udp_var.h" /* UDP prototypes */ +#include "igmp.h" +#include "igmp_var.h" /* IGMP prototypes */ + #include "tcp.h" #include "tcp_fsm.h" #include "tcp_seq.h" @@ -160,6 +163,13 @@ struct in_protosw inetsw[] = { eonprotoinit, 0, 0, 0, }, #endif +#ifdef MULTICAST +{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR, + igmp_input, rip_output, 0, rip_ctloutput, + rip_usrreq, + igmp_init, igmp_fasttimo, 0, 0, +}, +#endif #ifdef NSIP { SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR, idpip_input, rip_output, nsip_ctlinput, 0, diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 9e03a296adb49..70d7a8185f011 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)in_var.h 7.6 (Berkeley) 6/28/90 - * $Id: in_var.h,v 1.6 1993/12/19 21:43:26 wollman Exp $ + * $Id: in_var.h,v 1.7 1994/05/17 22:31:08 jkh Exp $ */ #ifndef _NETINET_IN_VAR_H_ @@ -58,6 +58,7 @@ struct in_ifaddr { struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */ #define ia_broadaddr ia_dstaddr struct sockaddr_in ia_sockmask; /* reserve space for general netmask */ + struct in_multi *ia_multiaddrs; /* list of multicast addresses */ }; struct in_aliasreq { @@ -144,5 +145,119 @@ extern int in_routemtu(struct route *); extern void in_mtureduce(struct in_addr, unsigned); extern void in_mtutimer(caddr_t, int); #endif /* MTUDISC */ + +/* + * Macro for finding the interface (ifnet structure) corresponding to one + * of our IP addresses. + */ +#define INADDR_TO_IFP(addr, ifp) \ + /* struct in_addr addr; */ \ + /* struct ifnet *ifp; */ \ +{ \ + register struct in_ifaddr *ia; \ +\ + for (ia = in_ifaddr; \ + ia != NULL && IA_SIN(ia)->sin_addr.s_addr != (addr).s_addr; \ + ia = ia->ia_next) \ + continue; \ + (ifp) = (ia == NULL) ? NULL : ia->ia_ifp; \ +} + +/* + * Macro for finding the internet address structure (in_ifaddr) corresponding + * to a given interface (ifnet structure). + */ +#define IFP_TO_IA(ifp, ia) \ + /* struct ifnet *ifp; */ \ + /* struct in_ifaddr *ia; */ \ +{ \ + for ((ia) = in_ifaddr; \ + (ia) != NULL && (ia)->ia_ifp != (ifp); \ + (ia) = (ia)->ia_next) \ + continue; \ +} +#endif + +/* + * Internet multicast address structure. There is one of these for each IP + * multicast group to which this host belongs on a given network interface. + * They are kept in a linked list, rooted in the interface's in_ifaddr + * structure. + */ +struct in_multi { + struct in_addr inm_addr; /* IP multicast address */ + struct ifnet *inm_ifp; /* back pointer to ifnet */ + struct in_ifaddr *inm_ia; /* back pointer to in_ifaddr */ + u_int inm_refcount; /* no. membership claims by sockets */ + u_int inm_timer; /* IGMP membership report timer */ + struct in_multi *inm_next; /* ptr to next multicast address */ +}; + +#ifdef KERNEL +/* + * Structure used by macros below to remember position when stepping through + * all of the in_multi records. + */ +struct in_multistep { + struct in_ifaddr *i_ia; + struct in_multi *i_inm; +}; + +/* + * Macro for looking up the in_multi record for a given IP multicast address + * on a given interface. If no matching record is found, "inm" returns NULL. + */ +#define IN_LOOKUP_MULTI(addr, ifp, inm) \ + /* struct in_addr addr; */ \ + /* struct ifnet *ifp; */ \ + /* struct in_multi *inm; */ \ +{ \ + register struct in_ifaddr *ia; \ +\ + IFP_TO_IA((ifp), ia); \ + if (ia == NULL) \ + (inm) = NULL; \ + else \ + for ((inm) = ia->ia_multiaddrs; \ + (inm) != NULL && (inm)->inm_addr.s_addr != (addr).s_addr; \ + (inm) = inm->inm_next) \ + continue; \ +} + +/* + * Macro to step through all of the in_multi records, one at a time. + * The current position is remembered in "step", which the caller must + * provide. IN_FIRST_MULTI(), below, must be called to initialize "step" + * and get the first record. Both macros return a NULL "inm" when there + * are no remaining records. + */ +#define IN_NEXT_MULTI(step, inm) \ + /* struct in_multistep step; */ \ + /* struct in_multi *inm; */ \ +{ \ + if (((inm) = (step).i_inm) != NULL) \ + (step).i_inm = (inm)->inm_next; \ + else \ + while ((step).i_ia != NULL) { \ + (inm) = (step).i_ia->ia_multiaddrs; \ + (step).i_ia = (step).i_ia->ia_next; \ + if ((inm) != NULL) { \ + (step).i_inm = (inm)->inm_next; \ + break; \ + } \ + } \ +} + +#define IN_FIRST_MULTI(step, inm) \ + /* struct in_multistep step; */ \ + /* struct in_multi *inm; */ \ +{ \ + (step).i_ia = in_ifaddr; \ + (step).i_inm = NULL; \ + IN_NEXT_MULTI((step), (inm)); \ +} + +struct in_multi *in_addmulti __P((struct in_addr *, struct ifnet *)); +int in_delmulti __P((struct in_multi *)); #endif /* KERNEL */ #endif /* _NETINET_IN_VAR_H_ */ diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index ed9b199e1db40..c192ef2d908b6 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)ip_icmp.c 7.15 (Berkeley) 4/20/91 - * $Id: ip_icmp.c,v 1.6 1993/12/19 00:52:42 wollman Exp $ + * $Id: ip_icmp.c,v 1.7 1994/05/17 22:31:09 jkh Exp $ */ #include "param.h" @@ -156,6 +156,11 @@ icmp_error(n, type, code, dest, mtu) icmpstat.icps_oldicmp++; goto freeit; } +#ifdef MULTICAST + /* Don't send error in response to a multicast or broadcast packet */ + if(n->m_flags & (M_MCAST | M_BCAST)) + goto freeit; +#endif /* * First, formulate icmp message diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 6c45aed63234a..426e757824d5f 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 1982, 1986, 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1982, 1986, 1988, 1993 + * Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)ip_input.c 7.19 (Berkeley) 5/25/91 - * $Id: ip_input.c,v 1.8 1994/01/04 17:47:13 ache Exp $ + * $Id: ip_input.c,v 1.9 1994/05/17 22:31:10 jkh Exp $ */ #include "param.h" @@ -240,6 +240,53 @@ next: goto ours; } } +#ifdef MULTICAST + if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { + struct in_multi *inm; +#ifdef MROUTING + extern struct socket *ip_mrouter; + + if (ip_mrouter) { + /* + * If we are acting as a multicast router, all + * incoming multicast packets are passed to the + * kernel-level multicast forwarding function. + * The packet is returned (relatively) intact; if + * ip_mforward() returns a non-zero value, the packet + * must be discarded, else it may be accepted below. + * + * (The IP ident field is put in the same byte order + * as expected when ip_mforward() is called from + * ip_output().) + */ + ip->ip_id = htons(ip->ip_id); + if (ip_mforward(ip, m->m_pkthdr.rcvif, m) != 0) { + m_freem(m); + goto next; + } + ip->ip_id = ntohs(ip->ip_id); + + /* + * The process-level routing demon needs to receive + * all multicast IGMP packets, whether or not this + * host belongs to their destination groups. + */ + if (ip->ip_p == IPPROTO_IGMP) + goto ours; + } +#endif + /* + * See if we belong to the destination multicast group on the + * arrival interface. + */ + IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm); + if (inm == NULL) { + m_freem(m); + goto next; + } + goto ours; + } +#endif if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST) goto ours; if (ip->ip_dst.s_addr == INADDR_ANY) diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c new file mode 100644 index 0000000000000..7ce8361ea3b90 --- /dev/null +++ b/sys/netinet/ip_mroute.c @@ -0,0 +1,1056 @@ +/* + * Copyright (c) 1989 Stephen Deering + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_mroute.c 7.4 (Berkeley) 11/19/92 + */ + +/* + * Procedures for the kernel part of DVMRP, + * a Distance-Vector Multicast Routing Protocol. + * (See RFC-1075.) + * + * Written by David Waitzman, BBN Labs, August 1988. + * Modified by Steve Deering, Stanford, February 1989. + * + * MROUTING 1.1 + */ + +#ifndef MROUTING +int ip_mrtproto; /* for netstat only */ +#else + +#include "param.h" +#include "errno.h" +#include "ioctl.h" +#include "malloc.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" +#include "time.h" + +#ifndef __FreeBSD__ +#include "net/af.h" +#else +#include "systm.h" +#endif +#include "net/if.h" +#include "net/route.h" +#include "net/raw_cb.h" + +#include "in.h" +#include "in_systm.h" +#include "ip.h" +#include "in_pcb.h" +#include "in_var.h" +#include "ip_var.h" + +#include "igmp.h" +#include "igmp_var.h" +#include "ip_mroute.h" + +/* Static forwards */ +static int ip_mrouter_init __P((struct socket *)); +static int add_vif __P((struct vifctl *)); +static int del_vif __P((vifi_t *vifip)); +static int add_lgrp __P((struct lgrplctl *)); +static int del_lgrp __P((struct lgrplctl *)); +static int grplst_member __P((struct vif *, struct in_addr)); +static u_long nethash __P((u_long in)); +static int add_mrt __P((struct mrtctl *)); +static int del_mrt __P((struct in_addr *)); +static struct mrt *mrtfind __P((u_long)); +static void phyint_send __P((struct ip *, struct vif *, struct mbuf *)); +static void srcrt_send __P((struct ip *, struct vif *, struct mbuf *)); +static void encap_send __P((struct ip *, struct vif *, struct mbuf *)); +static void multiencap_decap __P((struct mbuf *, int hlen)); + +#define INSIZ sizeof(struct in_addr) +#define same(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), INSIZ) == 0) +#define satosin(sa) ((struct sockaddr_in *)(sa)) + +/* + * Globals. All but ip_mrouter and ip_mrtproto could be static, + * except for netstat or debugging purposes. + */ +struct socket *ip_mrouter = NULL; +int ip_mrtproto = IGMP_DVMRP; /* for netstat only */ + +struct mrt *mrttable[MRTHASHSIZ]; +struct vif viftable[MAXVIFS]; +struct mrtstat mrtstat; + +/* + * 'Interfaces' associated with decapsulator (so we can tell + * packets that went through it from ones that get reflected + * by a broken gateway). These interfaces are never linked into + * the system ifnet list & no routes point to them. I.e., packets + * can't be sent this way. They only exist as a placeholder for + * multicast source verification. + */ +struct ifnet multicast_decap_if[MAXVIFS]; + +#define ENCAP_TTL 64 +#define ENCAP_PROTO 4 + +/* prototype IP hdr for encapsulated packets */ +struct ip multicast_encap_iphdr = { +#if defined(ultrix) || defined(i386) + sizeof(struct ip) >> 2, IPVERSION, +#else + IPVERSION, sizeof(struct ip) >> 2, +#endif + 0, /* tos */ + sizeof(struct ip), /* total length */ + 0, /* id */ + 0, /* frag offset */ + ENCAP_TTL, ENCAP_PROTO, + 0, /* checksum */ +}; + +/* + * Private variables. + */ +static vifi_t numvifs = 0; +static struct mrt *cached_mrt = NULL; +static u_long cached_origin; +static u_long cached_originmask; + +static void (*encap_oldrawip)(); + +/* + * one-back cache used by multiencap_decap to locate a tunnel's vif + * given a datagram's src ip address. + */ +static u_long last_encap_src; +static struct vif *last_encap_vif; + +/* + * A simple hash function: returns MRTHASHMOD of the low-order octet of + * the argument's network or subnet number. + */ +static u_long +nethash(n) + u_long n; +{ + struct in_addr in; + + in.s_addr = n; + n = in_netof(in); + while ((n & 0xff) == 0) + n >>= 8; + return (MRTHASHMOD(n)); +} + +/* + * this is a direct-mapped cache used to speed the mapping from a + * datagram source address to the associated multicast route. Note + * that unlike mrttable, the hash is on IP address, not IP net number. + */ +#define MSRCHASHSIZ 1024 +#define MSRCHASH(a) ((((a) >> 20) ^ ((a) >> 10) ^ (a)) & (MSRCHASHSIZ - 1)) +struct mrt *mrtsrchash[MSRCHASHSIZ]; + +/* + * Find a route for a given origin IP address. + */ +#define MRTFIND(o, rt) { \ + register u_int _mrhash = o; \ + _mrhash = MSRCHASH(_mrhash); \ + ++mrtstat.mrts_mrt_lookups; \ + rt = mrtsrchash[_mrhash]; \ + if (rt == NULL || \ + (o & rt->mrt_originmask.s_addr) != rt->mrt_origin.s_addr) \ + if ((rt = mrtfind(o)) != NULL) \ + mrtsrchash[_mrhash] = rt; \ +} + +static struct mrt * +mrtfind(origin) + u_long origin; +{ + register struct mrt *rt; + register u_int hash; + + mrtstat.mrts_mrt_misses++; + + hash = nethash(origin); + for (rt = mrttable[hash]; rt; rt = rt->mrt_next) { + if ((origin & rt->mrt_originmask.s_addr) == + rt->mrt_origin.s_addr) + return (rt); + } + return (NULL); +} + +/* + * Handle DVMRP setsockopt commands to modify the multicast routing tables. + */ +int +ip_mrouter_cmd(cmd, so, m) + register int cmd; + register struct socket *so; + register struct mbuf *m; +{ + register int error = 0; + + if (cmd != DVMRP_INIT && so != ip_mrouter) + error = EACCES; + else switch (cmd) { + + case DVMRP_INIT: + error = ip_mrouter_init(so); + break; + + case DVMRP_DONE: + error = ip_mrouter_done(); + break; + + case DVMRP_ADD_VIF: + if (m == NULL || m->m_len < sizeof(struct vifctl)) + error = EINVAL; + else + error = add_vif(mtod(m, struct vifctl *)); + break; + + case DVMRP_DEL_VIF: + if (m == NULL || m->m_len < sizeof(short)) + error = EINVAL; + else + error = del_vif(mtod(m, vifi_t *)); + break; + + case DVMRP_ADD_LGRP: + if (m == NULL || m->m_len < sizeof(struct lgrplctl)) + error = EINVAL; + else + error = add_lgrp(mtod(m, struct lgrplctl *)); + break; + + case DVMRP_DEL_LGRP: + if (m == NULL || m->m_len < sizeof(struct lgrplctl)) + error = EINVAL; + else + error = del_lgrp(mtod(m, struct lgrplctl *)); + break; + + case DVMRP_ADD_MRT: + if (m == NULL || m->m_len < sizeof(struct mrtctl)) + error = EINVAL; + else + error = add_mrt(mtod(m, struct mrtctl *)); + break; + + case DVMRP_DEL_MRT: + if (m == NULL || m->m_len < sizeof(struct in_addr)) + error = EINVAL; + else + error = del_mrt(mtod(m, struct in_addr *)); + break; + + default: + error = EOPNOTSUPP; + break; + } + return (error); +} + +/* + * Enable multicast routing + */ +static int +ip_mrouter_init(so) + register struct socket *so; +{ + if (so->so_type != SOCK_RAW || + so->so_proto->pr_protocol != IPPROTO_IGMP) + return (EOPNOTSUPP); + + if (ip_mrouter != NULL) + return (EADDRINUSE); + + ip_mrouter = so; + + return (0); +} + +/* + * Disable multicast routing + */ +int +ip_mrouter_done() +{ + register vifi_t vifi; + register int i; + register struct ifnet *ifp; + register int s; + struct ifreq ifr; + + s = splnet(); + + /* + * For each phyint in use, free its local group list and + * disable promiscuous reception of all IP multicasts. + */ + for (vifi = 0; vifi < numvifs; vifi++) { + if (viftable[vifi].v_lcl_addr.s_addr != 0 && + !(viftable[vifi].v_flags & VIFF_TUNNEL)) { + if (viftable[vifi].v_lcl_grps) + free(viftable[vifi].v_lcl_grps, M_MRTABLE); + satosin(&ifr.ifr_addr)->sin_family = AF_INET; + satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY; + ifp = viftable[vifi].v_ifp; + (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); + } + } + bzero((caddr_t)viftable, sizeof(viftable)); + numvifs = 0; + + /* + * Free any multicast route entries. + */ + for (i = 0; i < MRTHASHSIZ; i++) + if (mrttable[i]) + free(mrttable[i], M_MRTABLE); + bzero((caddr_t)mrttable, sizeof(mrttable)); + bzero((caddr_t)mrtsrchash, sizeof(mrtsrchash)); + + ip_mrouter = NULL; + + splx(s); + return (0); +} + +/* + * Add a vif to the vif table + */ +static int +add_vif(vifcp) + register struct vifctl *vifcp; +{ + register struct vif *vifp = viftable + vifcp->vifc_vifi; + register struct ifaddr *ifa; + register struct ifnet *ifp; + struct ifreq ifr; + register int error, s; + static struct sockaddr_in sin = { sizeof(sin), AF_INET }; + + if (vifcp->vifc_vifi >= MAXVIFS) + return (EINVAL); + if (vifp->v_lcl_addr.s_addr != 0) + return (EADDRINUSE); + + /* Find the interface with an address in AF_INET family */ + sin.sin_addr = vifcp->vifc_lcl_addr; + ifa = ifa_ifwithaddr((struct sockaddr *)&sin); + if (ifa == 0) + return (EADDRNOTAVAIL); + ifp = ifa->ifa_ifp; + + if (vifcp->vifc_flags & VIFF_TUNNEL) { + if ((vifcp->vifc_flags & VIFF_SRCRT) == 0) { + /* + * An encapsulating tunnel is wanted. If we + * haven't done so already, put our decap routine + * in front of raw_input so we have a chance to + * decapsulate incoming packets. Then set the + * arrival 'interface' to be the decapsulator. + */ + if (encap_oldrawip == 0) { + register int pr = ip_protox[ENCAP_PROTO]; + + encap_oldrawip = inetsw[pr].pr_input; + inetsw[pr].pr_input = multiencap_decap; + for (s = 0; s < MAXVIFS; ++s) { + multicast_decap_if[s].if_name = + "mdecap"; + multicast_decap_if[s].if_unit = s; + } + } + ifp = &multicast_decap_if[vifcp->vifc_vifi]; + } else { + ifp = 0; + } + } else { + /* Make sure the interface supports multicast */ + if ((ifp->if_flags & IFF_MULTICAST) == 0) + return EOPNOTSUPP; + + /* + * Enable promiscuous reception of all + * IP multicasts from the if + */ + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = + INADDR_ANY; + s = splnet(); + error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr); + splx(s); + if (error) + return error; + } + + s = splnet(); + vifp->v_flags = vifcp->vifc_flags; + vifp->v_threshold = vifcp->vifc_threshold; + vifp->v_lcl_addr = vifcp->vifc_lcl_addr; + vifp->v_ifp = ifa->ifa_ifp; + vifp->v_rmt_addr = vifcp->vifc_rmt_addr; + splx(s); + + /* Adjust numvifs up if the vifi is higher than numvifs */ + if (numvifs <= vifcp->vifc_vifi) + numvifs = vifcp->vifc_vifi + 1; + + splx(s); + return (0); +} + +/* + * Delete a vif from the vif table + */ +static int +del_vif(vifip) + register vifi_t *vifip; +{ + register struct vif *vifp = viftable + *vifip; + register struct ifnet *ifp; + register int i, s; + struct ifreq ifr; + + if (*vifip >= numvifs) + return (EINVAL); + if (vifp->v_lcl_addr.s_addr == 0) + return (EADDRNOTAVAIL); + + s = splnet(); + + if (!(vifp->v_flags & VIFF_TUNNEL)) { + if (vifp->v_lcl_grps) + free(vifp->v_lcl_grps, M_MRTABLE); + satosin(&ifr.ifr_addr)->sin_family = AF_INET; + satosin(&ifr.ifr_addr)->sin_addr.s_addr = INADDR_ANY; + ifp = vifp->v_ifp; + (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); + } + if (vifp == last_encap_vif) { + last_encap_vif = 0; + last_encap_src = 0; + } + bzero((caddr_t)vifp, sizeof (*vifp)); + + /* Adjust numvifs down */ + for (i = numvifs - 1; i >= 0; i--) + if (viftable[i].v_lcl_addr.s_addr != 0) + break; + numvifs = i + 1; + + splx(s); + return (0); +} + +/* + * Add the multicast group in the lgrpctl to the list of local multicast + * group memberships associated with the vif indexed by gcp->lgc_vifi. + */ +static int +add_lgrp(gcp) + register struct lgrplctl *gcp; +{ + register struct vif *vifp; + register int s; + + if (gcp->lgc_vifi >= numvifs) + return (EINVAL); + + vifp = viftable + gcp->lgc_vifi; + if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL)) + return (EADDRNOTAVAIL); + + /* If not enough space in existing list, allocate a larger one */ + s = splnet(); + if (vifp->v_lcl_grps_n + 1 >= vifp->v_lcl_grps_max) { + register int num; + register struct in_addr *ip; + + num = vifp->v_lcl_grps_max; + if (num <= 0) + num = 32; /* initial number */ + else + num += num; /* double last number */ + ip = (struct in_addr *)malloc(num * sizeof(*ip), + M_MRTABLE, M_NOWAIT); + if (ip == NULL) { + splx(s); + return (ENOBUFS); + } + + bzero((caddr_t)ip, num * sizeof(*ip)); /* XXX paranoid */ + bcopy((caddr_t)vifp->v_lcl_grps, (caddr_t)ip, + vifp->v_lcl_grps_n * sizeof(*ip)); + + vifp->v_lcl_grps_max = num; + if (vifp->v_lcl_grps) + free(vifp->v_lcl_grps, M_MRTABLE); + vifp->v_lcl_grps = ip; + } + + vifp->v_lcl_grps[vifp->v_lcl_grps_n++] = gcp->lgc_gaddr; + + if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group) + vifp->v_cached_result = 1; + + splx(s); + return (0); +} + +/* + * Delete the the local multicast group associated with the vif + * indexed by gcp->lgc_vifi. + */ + +static int +del_lgrp(gcp) + register struct lgrplctl *gcp; +{ + register struct vif *vifp; + register int i, error, s; + + if (gcp->lgc_vifi >= numvifs) + return (EINVAL); + vifp = viftable + gcp->lgc_vifi; + if (vifp->v_lcl_addr.s_addr == 0 || (vifp->v_flags & VIFF_TUNNEL)) + return (EADDRNOTAVAIL); + + s = splnet(); + + if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group) + vifp->v_cached_result = 0; + + error = EADDRNOTAVAIL; + for (i = 0; i < vifp->v_lcl_grps_n; ++i) + if (same(&gcp->lgc_gaddr, &vifp->v_lcl_grps[i])) { + error = 0; + --vifp->v_lcl_grps_n; + for (; i < vifp->v_lcl_grps_n; ++i) + vifp->v_lcl_grps[i] = vifp->v_lcl_grps[i + 1]; + error = 0; + break; + } + + splx(s); + return (error); +} + +/* + * Return 1 if gaddr is a member of the local group list for vifp. + */ +static int +grplst_member(vifp, gaddr) + register struct vif *vifp; + struct in_addr gaddr; +{ + register int i, s; + register u_long addr; + + mrtstat.mrts_grp_lookups++; + + addr = gaddr.s_addr; + if (addr == vifp->v_cached_group) + return (vifp->v_cached_result); + + mrtstat.mrts_grp_misses++; + + for (i = 0; i < vifp->v_lcl_grps_n; ++i) + if (addr == vifp->v_lcl_grps[i].s_addr) { + s = splnet(); + vifp->v_cached_group = addr; + vifp->v_cached_result = 1; + splx(s); + return (1); + } + s = splnet(); + vifp->v_cached_group = addr; + vifp->v_cached_result = 0; + splx(s); + return (0); +} + +/* + * Add an mrt entry + */ +static int +add_mrt(mrtcp) + register struct mrtctl *mrtcp; +{ + struct mrt *rt; + u_long hash; + int s; + + if (rt = mrtfind(mrtcp->mrtc_origin.s_addr)) { + /* Just update the route */ + s = splnet(); + rt->mrt_parent = mrtcp->mrtc_parent; + VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children); + VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves); + splx(s); + return (0); + } + + s = splnet(); + + rt = (struct mrt *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT); + if (rt == NULL) { + splx(s); + return (ENOBUFS); + } + + /* + * insert new entry at head of hash chain + */ + rt->mrt_origin = mrtcp->mrtc_origin; + rt->mrt_originmask = mrtcp->mrtc_originmask; + rt->mrt_parent = mrtcp->mrtc_parent; + VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children); + VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves); + /* link into table */ + hash = nethash(mrtcp->mrtc_origin.s_addr); + rt->mrt_next = mrttable[hash]; + mrttable[hash] = rt; + + splx(s); + return (0); +} + +/* + * Delete an mrt entry + */ +static int +del_mrt(origin) + register struct in_addr *origin; +{ + register struct mrt *rt, *prev_rt; + register u_long hash = nethash(origin->s_addr); + register struct mrt **cmrt, **cmrtend; + register int s; + + for (prev_rt = rt = mrttable[hash]; rt; prev_rt = rt, rt = rt->mrt_next) + if (origin->s_addr == rt->mrt_origin.s_addr) + break; + if (!rt) + return (ESRCH); + + s = splnet(); + + cmrt = mrtsrchash; + cmrtend = cmrt + MSRCHASHSIZ; + for ( ; cmrt < cmrtend; ++cmrt) + if (*cmrt == rt) + *cmrt = 0; + + if (prev_rt == rt) + mrttable[hash] = rt->mrt_next; + else + prev_rt->mrt_next = rt->mrt_next; + free(rt, M_MRTABLE); + + splx(s); + return (0); +} + +/* + * IP multicast forwarding function. This function assumes that the packet + * pointed to by "ip" has arrived on (or is about to be sent to) the interface + * pointed to by "ifp", and the packet is to be relayed to other networks + * that have members of the packet's destination IP multicast group. + * + * The packet is returned unscathed to the caller, unless it is tunneled + * or erroneous, in which case a non-zero return value tells the caller to + * discard it. + */ + +#define IP_HDR_LEN 20 /* # bytes of fixed IP header (excluding options) */ +#define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */ + +int +ip_mforward(ip, ifp, m) + register struct ip *ip; + register struct ifnet *ifp; + register struct mbuf *m; +{ + register struct mrt *rt; + register struct vif *vifp; + register int vifi; + register u_char *ipoptions; + u_long tunnel_src; + + if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 || + (ipoptions = (u_char *)(ip + 1))[1] != IPOPT_LSRR ) { + /* + * Packet arrived via a physical interface. + */ + tunnel_src = 0; + } else { + /* + * Packet arrived through a tunnel. + * + * A tunneled packet has a single NOP option and a + * two-element loose-source-and-record-route (LSRR) + * option immediately following the fixed-size part of + * the IP header. At this point in processing, the IP + * header should contain the following IP addresses: + * + * original source - in the source address field + * destination group - in the destination address field + * remote tunnel end-point - in the first element of LSRR + * one of this host's addrs - in the second element of LSRR + * + * NOTE: RFC-1075 would have the original source and + * remote tunnel end-point addresses swapped. However, + * that could cause delivery of ICMP error messages to + * innocent applications on intermediate routing + * hosts! Therefore, we hereby change the spec. + */ + + /* + * Verify that the tunnel options are well-formed. + */ + if (ipoptions[0] != IPOPT_NOP || + ipoptions[2] != 11 || /* LSRR option length */ + ipoptions[3] != 12 || /* LSRR address pointer */ + (tunnel_src = *(u_long *)(&ipoptions[4])) == 0) { + mrtstat.mrts_bad_tunnel++; + return (1); + } + + /* + * Delete the tunnel options from the packet. + */ + ovbcopy((caddr_t)(ipoptions + TUNNEL_LEN), (caddr_t)ipoptions, + (unsigned)(m->m_len - (IP_HDR_LEN + TUNNEL_LEN))); + m->m_len -= TUNNEL_LEN; + ip->ip_len -= TUNNEL_LEN; + ip->ip_hl -= TUNNEL_LEN >> 2; + } + + /* + * Don't forward a packet with time-to-live of zero or one, + * or a packet destined to a local-only group. + */ + if (ip->ip_ttl <= 1 || + ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP) + return ((int)tunnel_src); + + /* + * Don't forward if we don't have a route for the packet's origin. + */ + MRTFIND(ip->ip_src.s_addr, rt) + if (rt == NULL) { + mrtstat.mrts_no_route++; + return ((int)tunnel_src); + } + + /* + * Don't forward if it didn't arrive from the + * parent vif for its origin. + * + * Notes: v_ifp is zero for src route tunnels, multicast_decap_if + * for encapsulated tunnels and a real ifnet for non-tunnels so + * the first part of the if catches wrong physical interface or + * tunnel type; v_rmt_addr is zero for non-tunneled packets so + * the 2nd part catches both packets that arrive via a tunnel + * that shouldn't and packets that arrive via the wrong tunnel. + */ + vifi = rt->mrt_parent; + if (viftable[vifi].v_ifp != ifp || + (ifp == 0 && viftable[vifi].v_rmt_addr.s_addr != tunnel_src)) { + /* came in the wrong interface */ + ++mrtstat.mrts_wrong_if; + return (int)tunnel_src; + } + + /* + * For each vif, decide if a copy of the packet should be forwarded. + * Forward if: + * - the ttl exceeds the vif's threshold AND + * - the vif is a child in the origin's route AND + * - ( the vif is not a leaf in the origin's route OR + * the destination group has members on the vif ) + * + * (This might be speeded up with some sort of cache -- someday.) + */ + for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++) { + if (ip->ip_ttl > vifp->v_threshold && + VIFM_ISSET(vifi, rt->mrt_children) && + (!VIFM_ISSET(vifi, rt->mrt_leaves) || + grplst_member(vifp, ip->ip_dst))) { + if (vifp->v_flags & VIFF_SRCRT) + srcrt_send(ip, vifp, m); + else if (vifp->v_flags & VIFF_TUNNEL) + encap_send(ip, vifp, m); + else + phyint_send(ip, vifp, m); + } + } + return ((int)tunnel_src); +} + +static void +phyint_send(ip, vifp, m) + register struct ip *ip; + register struct vif *vifp; + register struct mbuf *m; +{ + register struct mbuf *mb_copy; + register struct ip_moptions *imo; + register int error; + struct ip_moptions simo; + + mb_copy = m_copy(m, 0, M_COPYALL); + if (mb_copy == NULL) + return; + + imo = &simo; + imo->imo_multicast_ifp = vifp->v_ifp; + imo->imo_multicast_ttl = ip->ip_ttl - 1; + imo->imo_multicast_loop = 1; + + error = ip_output(mb_copy, NULL, NULL, + IP_FORWARDING|IP_MULTICASTOPTS, imo); +} + +static void +srcrt_send(ip, vifp, m) + register struct ip *ip; + register struct vif *vifp; + register struct mbuf *m; +{ + register struct mbuf *mb_copy, *mb_opts; + register struct ip *ip_copy; + register int error; + register u_char *cp; + + /* + * Make sure that adding the tunnel options won't exceed the + * maximum allowed number of option bytes. + */ + if (ip->ip_hl > (60 - TUNNEL_LEN) >> 2) { + mrtstat.mrts_cant_tunnel++; + return; + } + + mb_copy = m_copy(m, 0, M_COPYALL); + if (mb_copy == NULL) + return; + ip_copy = mtod(mb_copy, struct ip *); + ip_copy->ip_ttl--; + ip_copy->ip_dst = vifp->v_rmt_addr; /* remote tunnel end-point */ + /* + * Adjust the ip header length to account for the tunnel options. + */ + ip_copy->ip_hl += TUNNEL_LEN >> 2; + ip_copy->ip_len += TUNNEL_LEN; + MGETHDR(mb_opts, M_DONTWAIT, MT_HEADER); + if (mb_opts == NULL) { + m_freem(mb_copy); + return; + } + /* + * 'Delete' the base ip header from the mb_copy chain + */ + mb_copy->m_len -= IP_HDR_LEN; + mb_copy->m_data += IP_HDR_LEN; + /* + * Make mb_opts be the new head of the packet chain. + * Any options of the packet were left in the old packet chain head + */ + mb_opts->m_next = mb_copy; + mb_opts->m_len = IP_HDR_LEN + TUNNEL_LEN; + mb_opts->m_data += MSIZE - mb_opts->m_len; + /* + * Copy the base ip header from the mb_copy chain to the new head mbuf + */ + bcopy((caddr_t)ip_copy, mtod(mb_opts, caddr_t), IP_HDR_LEN); + /* + * Add the NOP and LSRR after the base ip header + */ + cp = mtod(mb_opts, u_char *) + IP_HDR_LEN; + *cp++ = IPOPT_NOP; + *cp++ = IPOPT_LSRR; + *cp++ = 11; /* LSRR option length */ + *cp++ = 8; /* LSSR pointer to second element */ + *(u_long*)cp = vifp->v_lcl_addr.s_addr; /* local tunnel end-point */ + cp += 4; + *(u_long*)cp = ip->ip_dst.s_addr; /* destination group */ + + error = ip_output(mb_opts, NULL, NULL, IP_FORWARDING, NULL); +} + +static void +encap_send(ip, vifp, m) + register struct ip *ip; + register struct vif *vifp; + register struct mbuf *m; +{ + register struct mbuf *mb_copy; + register struct ip *ip_copy; + register int i, len = ip->ip_len; + + /* + * copy the old packet & pullup it's IP header into the + * new mbuf so we can modify it. Try to fill the new + * mbuf since if we don't the ethernet driver will. + */ + MGETHDR(mb_copy, M_DONTWAIT, MT_HEADER); + if (mb_copy == NULL) + return; + mb_copy->m_data += 16; + mb_copy->m_len = sizeof(multicast_encap_iphdr); + if ((mb_copy->m_next = m_copy(m, 0, M_COPYALL)) == NULL) { + m_freem(mb_copy); + return; + } + i = MHLEN - 16; + if (i > len) + i = len; + mb_copy = m_pullup(mb_copy, i); + if (mb_copy == NULL) + return; + + /* + * fill in the encapsulating IP header. + */ + ip_copy = mtod(mb_copy, struct ip *); + *ip_copy = multicast_encap_iphdr; + ip_copy->ip_id = htons(ip_id++); + ip_copy->ip_len += len; + ip_copy->ip_src = vifp->v_lcl_addr; + ip_copy->ip_dst = vifp->v_rmt_addr; + + /* + * turn the encapsulated IP header back into a valid one. + */ + ip = (struct ip *)((caddr_t)ip_copy + sizeof(multicast_encap_iphdr)); + --ip->ip_ttl; + HTONS(ip->ip_len); + HTONS(ip->ip_off); + ip->ip_sum = 0; +#if defined(LBL) && !defined(ultrix) && !defined(i386) + ip->ip_sum = ~oc_cksum((caddr_t)ip, ip->ip_hl << 2, 0); +#else + mb_copy->m_data += sizeof(multicast_encap_iphdr); + ip->ip_sum = in_cksum(mb_copy, ip->ip_hl << 2); + mb_copy->m_data -= sizeof(multicast_encap_iphdr); +#endif + ip_output(mb_copy, (struct mbuf *)0, (struct route *)0, + IP_FORWARDING, (struct mbuf *)0); +} + +/* + * De-encapsulate a packet and feed it back through ip input (this + * routine is called whenever IP gets a packet with proto type + * ENCAP_PROTO and a local destination address). + */ +static void +multiencap_decap(m, hlen) + register struct mbuf *m; + int hlen; +{ + struct ifnet *ifp; + register struct ip *ip = mtod(m, struct ip *); + register int s; + register struct ifqueue *ifq; + register struct vif *vifp; + + if (ip->ip_p != ENCAP_PROTO) { + (*encap_oldrawip)(m, hlen); + return; + } + /* + * dump the packet if it's not to a multicast destination or if + * we don't have an encapsulating tunnel with the source. + * Note: This code assumes that the remote site IP address + * uniquely identifies the tunnel (i.e., that this site has + * at most one tunnel with the remote site). + */ + if (! IN_MULTICAST(ntohl(((struct ip *)((char *)ip + hlen))->ip_dst.s_addr))) { + ++mrtstat.mrts_bad_tunnel; + m_freem(m); + return; + } + if (ip->ip_src.s_addr != last_encap_src) { + register struct vif *vife; + + vifp = viftable; + vife = vifp + numvifs; + last_encap_src = ip->ip_src.s_addr; + last_encap_vif = 0; + for ( ; vifp < vife; ++vifp) + if (vifp->v_rmt_addr.s_addr == ip->ip_src.s_addr) { + if ((vifp->v_flags & (VIFF_TUNNEL|VIFF_SRCRT)) + == VIFF_TUNNEL) + last_encap_vif = vifp; + break; + } + } + if ((vifp = last_encap_vif) == 0) { + mrtstat.mrts_cant_tunnel++; /*XXX*/ + m_freem(m); + return; + } + ifp = vifp->v_ifp; + m->m_data += hlen; + m->m_len -= hlen; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len -= hlen; + ifq = &ipintrq; + s = splimp(); + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + m_freem(m); + } else { + IF_ENQUEUE(ifq, m); + /* + * normally we would need a "schednetisr(NETISR_IP)" + * here but we were called by ip_input and it is going + * to loop back & try to dequeue the packet we just + * queued as soon as we return so we avoid the + * unnecessary software interrrupt. + */ + } + splx(s); +} +#endif diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h new file mode 100644 index 0000000000000..4b3c753998dfc --- /dev/null +++ b/sys/netinet/ip_mroute.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 1989 Stephen Deering. + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_mroute.h 7.2 (Berkeley) 7/8/92 + */ + +/* + * Definitions for the kernel part of DVMRP, + * a Distance-Vector Multicast Routing Protocol. + * (See RFC-1075.) + * + * Written by David Waitzman, BBN Labs, August 1988. + * Modified by Steve Deering, Stanford, February 1989. + * + * MROUTING 1.0 + */ + + +/* + * DVMRP-specific setsockopt commands. + */ +#define DVMRP_INIT 100 +#define DVMRP_DONE 101 +#define DVMRP_ADD_VIF 102 +#define DVMRP_DEL_VIF 103 +#define DVMRP_ADD_LGRP 104 +#define DVMRP_DEL_LGRP 105 +#define DVMRP_ADD_MRT 106 +#define DVMRP_DEL_MRT 107 + + +/* + * Types and macros for handling bitmaps with one bit per virtual interface. + */ +#define MAXVIFS 32 +typedef u_long vifbitmap_t; +typedef u_short vifi_t; /* type of a vif index */ + +#define VIFM_SET(n, m) ((m) |= (1 << (n))) +#define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) +#define VIFM_ISSET(n, m) ((m) & (1 << (n))) +#define VIFM_CLRALL(m) ((m) = 0x00000000) +#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) +#define VIFM_SAME(m1, m2) ((m1) == (m2)) + + +/* + * Agument structure for DVMRP_ADD_VIF. + * (DVMRP_DEL_VIF takes a single vifi_t argument.) + */ +struct vifctl { + vifi_t vifc_vifi; /* the index of the vif to be added */ + u_char vifc_flags; /* VIFF_ flags defined below */ + u_char vifc_threshold; /* min ttl required to forward on vif */ + struct in_addr vifc_lcl_addr; /* local interface address */ + struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */ +}; + +#define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */ +#define VIFF_SRCRT 0x2 /* tunnel uses IP src routing */ + + +/* + * Argument structure for DVMRP_ADD_LGRP and DVMRP_DEL_LGRP. + */ +struct lgrplctl { + vifi_t lgc_vifi; + struct in_addr lgc_gaddr; +}; + + +/* + * Argument structure for DVMRP_ADD_MRT. + * (DVMRP_DEL_MRT takes a single struct in_addr argument, containing origin.) + */ +struct mrtctl { + struct in_addr mrtc_origin; /* subnet origin of multicasts */ + struct in_addr mrtc_originmask; /* subnet mask for origin */ + vifi_t mrtc_parent; /* incoming vif */ + vifbitmap_t mrtc_children; /* outgoing children vifs */ + vifbitmap_t mrtc_leaves; /* subset of outgoing children vifs */ +}; + + +#ifdef KERNEL + +/* + * The kernel's virtual-interface structure. + */ +struct vif { + u_char v_flags; /* VIFF_ flags defined above */ + u_char v_threshold; /* min ttl required to forward on vif */ + struct in_addr v_lcl_addr; /* local interface address */ + struct in_addr v_rmt_addr; /* remote address (tunnels only) */ + struct ifnet *v_ifp; /* pointer to interface */ + struct in_addr *v_lcl_grps; /* list of local grps (phyints only) */ + int v_lcl_grps_max; /* malloc'ed number of v_lcl_grps */ + int v_lcl_grps_n; /* used number of v_lcl_grps */ + u_long v_cached_group; /* last grp looked-up (phyints only) */ + int v_cached_result; /* last look-up result (phyints only) */ +}; + +/* + * The kernel's multicast route structure. + */ +struct mrt { + struct in_addr mrt_origin; /* subnet origin of multicasts */ + struct in_addr mrt_originmask; /* subnet mask for origin */ + vifi_t mrt_parent; /* incoming vif */ + vifbitmap_t mrt_children; /* outgoing children vifs */ + vifbitmap_t mrt_leaves; /* subset of outgoing children vifs */ + struct mrt *mrt_next; /* forward link */ +}; + + +#define MRTHASHSIZ 256 +#if (MRTHASHSIZ & (MRTHASHSIZ - 1)) == 0 /* from sys:route.h */ +#define MRTHASHMOD(h) ((h) & (MRTHASHSIZ - 1)) +#else +#define MRTHASHMOD(h) ((h) % MRTHASHSIZ) +#endif + +/* + * The kernel's multicast routing statistics. + */ +struct mrtstat { + u_long mrts_mrt_lookups; /* # multicast route lookups */ + u_long mrts_mrt_misses; /* # multicast route cache misses */ + u_long mrts_grp_lookups; /* # group address lookups */ + u_long mrts_grp_misses; /* # group address cache misses */ + u_long mrts_no_route; /* no route for packet's origin */ + u_long mrts_bad_tunnel; /* malformed tunnel options */ + u_long mrts_cant_tunnel; /* no room for tunnel options */ + u_long mrts_wrong_if; /* arrived on wrong interface */ +}; + +int ip_mrouter_cmd __P((int, struct socket *, struct mbuf *)); +int ip_mrouter_done __P(()); + +#endif /* KERNEL */ + diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index e9b459e870b85..f1b9975133a86 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)ip_output.c 7.23 (Berkeley) 11/12/90 - * $Id: ip_output.c,v 1.5 1993/12/19 00:52:45 wollman Exp $ + * $Id: ip_output.c,v 1.7 1994/05/26 22:42:15 jkh Exp $ */ #include "param.h" @@ -58,6 +58,7 @@ #endif struct mbuf *ip_insertoptions(); +void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *)); /* * IP output. The packet in mbuf chain m contains a skeletal IP @@ -66,11 +67,18 @@ struct mbuf *ip_insertoptions(); * The mbuf opt, if present, will not be freed. */ int -ip_output(m0, opt, ro, flags) +ip_output(m0, opt, ro, flags +#ifdef MULTICAST + , imo +#endif + ) struct mbuf *m0; struct mbuf *opt; struct route *ro; int flags; +#ifdef MULTICAST + struct ip_moptions *imo; +#endif { register struct ip *ip, *mhip; register struct ifnet *ifp; @@ -152,6 +160,97 @@ ip_output(m0, opt, ro, flags) if (ro->ro_rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; } +#ifdef MULTICAST + if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { + struct in_multi *inm; + extern struct ifnet loif; + extern struct socket *ip_mrouter; + + m->m_flags |= M_MCAST; + /* + * IP destination address is multicast. Make sure "dst" + * still points to the address in "ro". (It may have been + * changed to point to a gateway address, above.) + */ + dst = (struct sockaddr_in *)&ro->ro_dst; + /* + * See if the caller provided any multicast options + */ + if ((flags & IP_MULTICASTOPTS) && imo != NULL) { + ip->ip_ttl = imo->imo_multicast_ttl; + if (imo->imo_multicast_ifp != NULL) + ifp = imo->imo_multicast_ifp; + } else { + imo = NULL; + ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; + } + /* + * Confirm that the outgoing interface supports multicast. + */ + if ((ifp->if_flags & IFF_MULTICAST) == 0) { + error = ENETUNREACH; + goto bad; + } + /* + * If source address not specified yet, use address + * of outgoing interface. + */ + if (ip->ip_src.s_addr == INADDR_ANY) { + register struct in_ifaddr *ia; + + for (ia = in_ifaddr; ia; ia = ia->ia_next) + if (ia->ia_ifp == ifp) { + ip->ip_src = IA_SIN(ia)->sin_addr; + break; + } + } + + IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); + if (inm != NULL && + (imo == NULL || imo->imo_multicast_loop)) { + /* + * If we belong to the destination multicast group + * on the outgoing interface, and the caller did not + * forbid loopback, loop back a copy. + */ + ip_mloopback(ifp, m, dst); + } +#ifdef MROUTING + else if (ip_mrouter && (flags & IP_FORWARDING) == 0) { + /* + * If we are acting as a multicast router, perform + * multicast forwarding as if the packet had just + * arrived on the interface to which we are about + * to send. The multicast forwarding function + * recursively calls this function, using the + * IP_FORWARDING flag to prevent infinite recursion. + * + * Multicasts that are looped back by ip_mloopback(), + * above, will be forwarded by the ip_input() routine, + * if necessary. + */ + if (ip_mforward(ip, ifp, m) != 0) { + m_freem(m); + goto done; + } + } +#endif + /* + * Multicasts with a time-to-live of zero may be looped- + * back, above, but must not be transmitted on a network. + * Also, multicasts addressed to the loopback interface + * are not sent -- the above call to ip_mloopback() will + * loop back a copy if this host actually belongs to the + * destination group on the loopback interface. + */ + if (ip->ip_ttl == 0 || ifp == &loif) { + m_freem(m); + goto done; + } + + goto sendit; + } +#endif #ifndef notdef /* * If source address not specified yet, use address @@ -192,7 +291,9 @@ ip_output(m0, opt, ro, flags) } m->m_flags |= M_BCAST; } - +#ifdef MULTICAST +sendit: +#endif /* * If small enough for interface, can just send directly. */ @@ -413,7 +514,24 @@ ip_ctloutput(op, so, level, optname, mp) case IP_RECVRETOPTS: case IP_RECVDSTADDR: if (m->m_len != sizeof(int)) +#if defined(MULTICAST) && defined(OLD_VAT_COMPAT) + { + optname += IP_RETOPTS-IP_OPTIONS; + switch(optname) { + case IP_MULTICAST_TTL: + case IP_MULTICAST_LOOP: + if (m->m_len == sizeof(char)) + goto multicast_setopt; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + if (m->m_len == sizeof(struct ip_mreq)) + goto multicast_setopt; + } + error = EINVAL; + } +#else error = EINVAL; +#endif else { optval = *mtod(m, int *); switch (optname) { @@ -446,7 +564,25 @@ ip_ctloutput(op, so, level, optname, mp) } break; #undef OPTSET - +#ifdef MULTICAST +#ifdef OLD_VAT_COMPAT + case IP_HDRINCL: + if (m->m_len != sizeof(struct in_addr)) { + error = EINVAL; + break; + } + optname = IP_MULTICAST_IF; + /* FALLTHRU */ +multicast_setopt: +#endif + case IP_MULTICAST_IF: + case IP_MULTICAST_TTL: + case IP_MULTICAST_LOOP: + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + error = ip_setmoptions(optname, &inp->inp_moptions, m); + break; +#endif default: error = EINVAL; break; @@ -501,7 +637,15 @@ ip_ctloutput(op, so, level, optname, mp) } *mtod(m, int *) = optval; break; - +#ifdef MULTICAST + case IP_MULTICAST_IF: + case IP_MULTICAST_TTL: + case IP_MULTICAST_LOOP: + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + error = ip_getmoptions(optname, inp->inp_moptions, mp); + break; +#endif default: error = EINVAL; break; @@ -617,3 +761,339 @@ bad: (void)m_free(m); return (EINVAL); } + +#ifdef MULTICAST +/* + * Set the IP multicast options in response to user setsockopt(). + */ +int +ip_setmoptions(optname, imop, m) + int optname; + struct ip_moptions **imop; + struct mbuf *m; +{ + register int error = 0; + u_char loop; + register int i; + struct in_addr addr; + register struct ip_mreq *mreq; + register struct ifnet *ifp; + register struct ip_moptions *imo = *imop; + struct route ro; + register struct sockaddr_in *dst; + + if (imo == NULL) { + /* + * No multicast option buffer attached to the pcb; + * allocate one and initialize to default values. + */ + imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS, + M_WAITOK); + + if (imo == NULL) + return (ENOBUFS); + *imop = imo; + imo->imo_multicast_ifp = NULL; + imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; + imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; + imo->imo_num_memberships = 0; + } + + switch (optname) { + + case IP_MULTICAST_IF: + /* + * Select the interface for outgoing multicast packets. + */ + if (m == NULL || m->m_len != sizeof(struct in_addr)) { + error = EINVAL; + break; + } + addr = *(mtod(m, struct in_addr *)); + /* + * INADDR_ANY is used to remove a previous selection. + * When no interface is selected, a default one is + * chosen every time a multicast packet is sent. + */ + if (addr.s_addr == INADDR_ANY) { + imo->imo_multicast_ifp = NULL; + break; + } + /* + * The selected interface is identified by its local + * IP address. Find the interface and confirm that + * it supports multicasting. + */ + INADDR_TO_IFP(addr, ifp); + if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { + error = EADDRNOTAVAIL; + break; + } + imo->imo_multicast_ifp = ifp; + break; + + case IP_MULTICAST_TTL: + /* + * Set the IP time-to-live for outgoing multicast packets. + */ + if (m == NULL || m->m_len != 1) { + error = EINVAL; + break; + } + imo->imo_multicast_ttl = *(mtod(m, u_char *)); + break; + + case IP_MULTICAST_LOOP: + /* + * Set the loopback flag for outgoing multicast packets. + * Must be zero or one. + */ + if (m == NULL || m->m_len != 1 || + (loop = *(mtod(m, u_char *))) > 1) { + error = EINVAL; + break; + } + imo->imo_multicast_loop = loop; + break; + + case IP_ADD_MEMBERSHIP: + /* + * Add a multicast group membership. + * Group must be a valid IP multicast address. + */ + if (m == NULL || m->m_len != sizeof(struct ip_mreq)) { + error = EINVAL; + break; + } + mreq = mtod(m, struct ip_mreq *); + if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) { + error = EINVAL; + break; + } + /* + * If no interface address was provided, use the interface of + * the route to the given multicast address. + */ + if (mreq->imr_interface.s_addr == INADDR_ANY) { + ro.ro_rt = NULL; + dst = (struct sockaddr_in *)&ro.ro_dst; + dst->sin_len = sizeof(*dst); + dst->sin_family = AF_INET; + dst->sin_addr = mreq->imr_multiaddr; + rtalloc(&ro); + if (ro.ro_rt == NULL) { + error = EADDRNOTAVAIL; + break; + } + ifp = ro.ro_rt->rt_ifp; + rtfree(ro.ro_rt); + } + else { + INADDR_TO_IFP(mreq->imr_interface, ifp); + } + /* + * See if we found an interface, and confirm that it + * supports multicast. + */ + if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { + error = EADDRNOTAVAIL; + break; + } + /* + * See if the membership already exists or if all the + * membership slots are full. + */ + for (i = 0; i < imo->imo_num_memberships; ++i) { + if (imo->imo_membership[i]->inm_ifp == ifp && + imo->imo_membership[i]->inm_addr.s_addr + == mreq->imr_multiaddr.s_addr) + break; + } + if (i < imo->imo_num_memberships) { + error = EADDRINUSE; + break; + } + if (i == IP_MAX_MEMBERSHIPS) { + error = ETOOMANYREFS; + break; + } + /* + * Everything looks good; add a new record to the multicast + * address list for the given interface. + */ + if ((imo->imo_membership[i] = + in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) { + error = ENOBUFS; + break; + } + ++imo->imo_num_memberships; + break; + + case IP_DROP_MEMBERSHIP: + /* + * Drop a multicast group membership. + * Group must be a valid IP multicast address. + */ + if (m == NULL || m->m_len != sizeof(struct ip_mreq)) { + error = EINVAL; + break; + } + mreq = mtod(m, struct ip_mreq *); + if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) { + error = EINVAL; + break; + } + /* + * If an interface address was specified, get a pointer + * to its ifnet structure. + */ + if (mreq->imr_interface.s_addr == INADDR_ANY) + ifp = NULL; + else { + INADDR_TO_IFP(mreq->imr_interface, ifp); + if (ifp == NULL) { + error = EADDRNOTAVAIL; + break; + } + } + /* + * Find the membership in the membership array. + */ + for (i = 0; i < imo->imo_num_memberships; ++i) { + if ((ifp == NULL || + imo->imo_membership[i]->inm_ifp == ifp) && + imo->imo_membership[i]->inm_addr.s_addr == + mreq->imr_multiaddr.s_addr) + break; + } + if (i == imo->imo_num_memberships) { + error = EADDRNOTAVAIL; + break; + } + /* + * Give up the multicast address record to which the + * membership points. + */ + in_delmulti(imo->imo_membership[i]); + /* + * Remove the gap in the membership array. + */ + for (++i; i < imo->imo_num_memberships; ++i) + imo->imo_membership[i-1] = imo->imo_membership[i]; + --imo->imo_num_memberships; + break; + + default: + error = EOPNOTSUPP; + break; + } + + /* + * If all options have default values, no need to keep the mbuf. + */ + if (imo->imo_multicast_ifp == NULL && + imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL && + imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP && + imo->imo_num_memberships == 0) { + free(*imop, M_IPMOPTS); + *imop = NULL; + } + + return (error); +} + +/* + * Return the IP multicast options in response to user getsockopt(). + */ +int +ip_getmoptions(optname, imo, mp) + int optname; + register struct ip_moptions *imo; + register struct mbuf **mp; +{ + u_char *ttl; + u_char *loop; + struct in_addr *addr; + struct in_ifaddr *ia; + + *mp = m_get(M_WAIT, MT_SOOPTS); + + switch (optname) { + + case IP_MULTICAST_IF: + addr = mtod(*mp, struct in_addr *); + (*mp)->m_len = sizeof(struct in_addr); + if (imo == NULL || imo->imo_multicast_ifp == NULL) + addr->s_addr = INADDR_ANY; + else { + IFP_TO_IA(imo->imo_multicast_ifp, ia); + addr->s_addr = (ia == NULL) ? INADDR_ANY + : IA_SIN(ia)->sin_addr.s_addr; + } + return (0); + + case IP_MULTICAST_TTL: + ttl = mtod(*mp, u_char *); + (*mp)->m_len = 1; + *ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL + : imo->imo_multicast_ttl; + return (0); + + case IP_MULTICAST_LOOP: + loop = mtod(*mp, u_char *); + (*mp)->m_len = 1; + *loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP + : imo->imo_multicast_loop; + return (0); + + default: + return (EOPNOTSUPP); + } +} + +/* + * Discard the IP multicast options. + */ +void +ip_freemoptions(imo) + register struct ip_moptions *imo; +{ + register int i; + + if (imo != NULL) { + for (i = 0; i < imo->imo_num_memberships; ++i) + in_delmulti(imo->imo_membership[i]); + free(imo, M_IPMOPTS); + } +} + +/* + * Routine called from ip_output() to loop back a copy of an IP multicast + * packet to the input queue of a specified interface. Note that this + * calls the output routine of the loopback "driver", but with an interface + * pointer that might NOT be &loif -- easier than replicating that code here. + */ +void +ip_mloopback(ifp, m, dst) + struct ifnet *ifp; + register struct mbuf *m; + register struct sockaddr_in *dst; +{ + register struct ip *ip; + struct mbuf *copym; + + copym = m_copy(m, 0, M_COPYALL); + if (copym != NULL) { + /* + * We don't bother to fragment if the IP length is greater + * than the interface's MTU. Can this possibly matter? + */ + ip = mtod(copym, struct ip *); + ip->ip_len = htons((u_short)ip->ip_len); + ip->ip_off = htons((u_short)ip->ip_off); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(copym, ip->ip_hl << 2); + (void) looutput(ifp, copym, (struct sockaddr *)dst, 0); + } +} +#endif diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index 47d76fe01b5a5..9452d8c9a5d50 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1982, 1986 Regents of the University of California. + * Copyright (c) 1982, 1986, 1993 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)ip_var.h 7.7 (Berkeley) 6/28/90 - * $Id: ip_var.h,v 1.3 1993/11/07 17:48:00 wollman Exp $ + * $Id: ip_var.h,v 1.4 1994/05/17 22:31:12 jkh Exp $ */ #ifndef _NETINET_IP_VAR_H_ @@ -103,6 +103,18 @@ struct ipoption { char ipopt_list[MAX_IPOPTLEN]; /* options proper */ }; +/* + * Structure attached to inpcb.ip_moptions and + * passed to ip_output when IP multicast options are in use. + */ +struct ip_moptions { + struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */ + u_char imo_multicast_ttl; /* TTL for outgoing multicasts */ + u_char imo_multicast_loop; /* 1 => hear sends if a member */ + u_short imo_num_memberships; /* no. memberships this socket */ + struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS]; +}; + struct ipstat { long ips_total; /* total packets received */ long ips_badsum; /* checksum bad */ @@ -129,6 +141,7 @@ struct ipstat { #ifdef KERNEL /* flags passed to ip_output as last parameter */ #define IP_FORWARDING 0x1 /* most of ip header exists */ +#define IP_MULTICASTOPTS 0x2 /* multicast opts present */ #define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */ #define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */ diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 1618339176ae9..a98cc1a71efe0 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 1982, 1986, 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1982, 1986, 1988, 1993 + * Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)raw_ip.c 7.8 (Berkeley) 7/25/90 - * $Id: raw_ip.c,v 1.4 1993/12/19 00:52:46 wollman Exp $ + * $Id: raw_ip.c,v 1.5 1994/05/17 22:31:13 jkh Exp $ */ #include "param.h" @@ -118,7 +118,11 @@ rip_output(m, so) return (ip_output(m, (rp->rinp_flags & RINPF_HDRINCL)? (struct mbuf *)0: rp->rinp_options, &rp->rinp_route, - (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST)); + (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST +#ifdef MULTICAST + | IP_MULTICASTOPTS, rp->rinp_rcb.rcb_moptions +#endif + )); } /* @@ -154,10 +158,27 @@ rip_ctloutput(op, so, level, optname, m) else rp->rinp_flags &= ~RINPF_HDRINCL; break; - +#ifdef MULTICAST + case IP_MULTICAST_IF: + case IP_MULTICAST_TTL: + case IP_MULTICAST_LOOP: + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + error = ip_setmoptions(optname, + &rp->rinp_rcb.rcb_moptions, *m); + break; + default: +#ifdef MROUTING + error = ip_mrouter_cmd(optname, so, *m); +#else + error = EINVAL; +#endif + break; +#else default: error = EINVAL; break; +#endif } break; @@ -178,7 +199,16 @@ rip_ctloutput(op, so, level, optname, m) (*m)->m_len = sizeof (int); *mtod(*m, int *) = rp->rinp_flags & RINPF_HDRINCL; break; - +#ifdef MULTICAST + case IP_MULTICAST_IF: + case IP_MULTICAST_TTL: + case IP_MULTICAST_LOOP: + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + error = ip_getmoptions(optname, + rp->rinp_rcb.rcb_moptions, m); + break; +#endif default: error = EINVAL; m_freem(*m); @@ -201,7 +231,9 @@ rip_usrreq(so, req, m, nam, control) { register int error = 0; register struct raw_inpcb *rp = sotorawinpcb(so); - +#if defined(MULTICAST) && defined(MROUTING) + extern struct socket *ip_mrouter; +#endif switch (req) { case PRU_ATTACH: @@ -217,8 +249,16 @@ rip_usrreq(so, req, m, nam, control) case PRU_DETACH: if (rp == 0) panic("rip_detach"); +#if defined(MULTICAST) && defined(MROUTING) + if (so == ip_mrouter) + ip_mrouter_done(); +#endif if (rp->rinp_options) m_freem(rp->rinp_options); +#ifdef MULTICAST + if (rp->rinp_rcb.rcb_moptions) + ip_freemoptions(rp->rinp_rcb.rcb_moptions); +#endif if (rp->rinp_route.ro_rt) RTFREE(rp->rinp_route.ro_rt); if (rp->rinp_rcb.rcb_laddr) diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index b6f377690856e..e258f5f6d022a 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)tcp_usrreq.c 7.15 (Berkeley) 6/28/90 - * $Id: tcp_usrreq.c,v 1.5 1994/01/24 05:12:36 davidg Exp $ + * $Id: tcp_usrreq.c,v 1.6 1994/05/22 23:18:40 ache Exp $ */ #include "param.h" @@ -351,12 +351,15 @@ tcp_ctloutput(op, so, level, optname, mp) { int error = 0; struct inpcb *inp = sotoinpcb(so); - register struct tcpcb *tp = intotcpcb(inp); + register struct tcpcb *tp; register struct mbuf *m; if (level != IPPROTO_TCP) return (ip_ctloutput(op, so, level, optname, mp)); + if (!inp || !(tp = intotcpcb(inp))) + return (EINVAL); + switch (op) { case PRCO_SETOPT: diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 44e244c31c956..12b5b3ece29e9 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1982, 1986, 1988, 1990, 1993 + * Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)udp_usrreq.c 7.20 (Berkeley) 4/20/91 - * $Id: udp_usrreq.c,v 1.7 1994/02/07 19:53:25 ache Exp $ + * $Id: udp_usrreq.c,v 1.8 1994/05/17 22:31:14 jkh Exp $ */ #include "param.h" @@ -145,6 +145,93 @@ udp_input(m, iphlen) return; } } +#ifdef MULTICAST + if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || + in_broadcast(ip->ip_dst)) { + struct socket *last; + /* + * Deliver a multicast or broadcast datagram to *all* sockets + * for which the local and remote addresses and ports match + * those of the incoming datagram. This allows more than + * one process to receive multi/broadcasts on the same port. + * (This really ought to be done for unicast datagrams as + * well, but that would cause problems with existing + * applications that open both address-specific sockets and + * a wildcard socket listening to the same port -- they would + * end up receiving duplicates of every unicast datagram. + * Those applications open the multiple sockets to overcome an + * inadequacy of the UDP socket interface, but for backwards + * compatibility we avoid the problem here rather than + * fixing the interface. Maybe 4.4BSD will remedy this?) + */ + + /* + * Construct sockaddr format source address. + */ + udp_in.sin_port = uh->uh_sport; + udp_in.sin_addr = ip->ip_src; + m->m_len -= sizeof (struct udpiphdr); + m->m_data += sizeof (struct udpiphdr); + /* + * Locate pcb(s) for datagram. + * (Algorithm copied from raw_intr().) + */ + last = NULL; + for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) { + if (inp->inp_lport != uh->uh_dport) + continue; + if (inp->inp_laddr.s_addr != INADDR_ANY) { + if (inp->inp_laddr.s_addr != + ip->ip_dst.s_addr) + continue; + } + if (inp->inp_faddr.s_addr != INADDR_ANY) { + if (inp->inp_faddr.s_addr != + ip->ip_src.s_addr || + inp->inp_fport != uh->uh_sport) + continue; + } + + if (last != NULL) { + struct mbuf *n; + + if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { + if (sbappendaddr(&last->so_rcv, + (struct sockaddr *)&udp_in, + n, (struct mbuf *)0) == 0) + m_freem(n); + else + sorwakeup(last); + } + } + last = inp->inp_socket; + /* + * Don't look for additional matches if this one + * does not have the SO_REUSEADDR socket option set. + * This heuristic avoids searching through all pcbs + * in the common case of a non-shared port. It + * assumes that an application will never clear + * the SO_REUSEADDR option after setting it. + */ + if ((last->so_options & SO_REUSEADDR) == 0) + break; + } + + if (last == NULL) { + /* + * No matching pcb found; discard datagram. + * (No need to send an ICMP Port Unreachable + * for a broadcast or multicast datgram.) + */ + goto bad; + } + if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in, + m, (struct mbuf *)0) == 0) + goto bad; + sorwakeup(last); + return; + } +#endif /* * Locate pcb for datagram. @@ -163,10 +250,13 @@ udp_input(m, iphlen) if (inp == 0) { /* don't send ICMP response for broadcast packet */ udpstat.udps_noport++; - if (m->m_flags & M_BCAST) { +#ifndef MULTICAST + /* XXX why don't we do this with MULTICAST? */ + if (m->m_flags & (M_BCAST | M_MCAST)) { udpstat.udps_noportbcast++; goto bad; } +#endif *ip = save_ip; ip->ip_len += iphlen; { @@ -355,7 +445,11 @@ udp_output(inp, m, addr, control) ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */ udpstat.udps_opackets++; error = ip_output(m, inp->inp_options, &inp->inp_route, - inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)); + inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST) +#ifdef MULTICAST + | IP_MULTICASTOPTS, inp->inp_moptions +#endif + ); if (addr) { in_pcbdisconnect(inp); |