Index: head/sys/net/bridge.c =================================================================== --- head/sys/net/bridge.c (revision 71958) +++ head/sys/net/bridge.c (revision 71959) @@ -1,851 +1,851 @@ /* * Copyright (c) 1998-2001 Luigi Rizzo * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. * * $FreeBSD$ */ /* * This code implements bridging in FreeBSD. It only acts on ethernet * type of interfaces (others are still usable for routing). * A bridging table holds the source MAC address/dest. interface for each * known node. The table is indexed using an hash of the source address. * * Input packets are tapped near the beginning of ether_input(), and * analysed by calling bridge_in(). Depending on the result, the packet * can be forwarded to one or more output interfaces using bdg_forward(), * and/or sent to the upper layer (e.g. in case of multicast). * * Output packets are intercepted near the end of ether_output(), * the correct destination is selected calling bridge_dst_lookup(), * and then forwarding is done using bdg_forward(). * Bridging is controlled by the sysctl variable net.link.ether.bridge * * The arp code is also modified to let a machine answer to requests * irrespective of the port the request came from. * * In case of loops in the bridging topology, the bridge detects this * event and temporarily mutes output bridging on one of the ports. * Periodically, interfaces are unmuted by bdg_timeout(). * Muting is only implemented as a safety measure, and also as * a mechanism to support a user-space implementation of the spanning * tree algorithm. In the final release, unmuting will only occur * because of explicit action of the user-level daemon. * * To build a bridging kernel, use the following option * option BRIDGE * and then at runtime set the sysctl variable to enable bridging. * * Only one interface is supposed to have addresses set (but * there are no problems in practice if you set addresses for more * than one interface). * Bridging will act before routing, but nothing prevents a machine * from doing both (modulo bugs in the implementation...). * * THINGS TO REMEMBER * - bridging is incompatible with multicast routing on the same * machine. There is not an easy fix to this. * - loop detection is still not very robust. * - the interface of bdg_forward() could be improved. */ #include #include #include #include #include /* for net/if.h */ #include #include #include #include #include /* for struct arpcom */ #include #include #include #include /* for struct arpcom */ #include "opt_ipfw.h" #include "opt_ipdn.h" #if defined(IPFIREWALL) #include #include #if defined(DUMMYNET) #include #endif #endif #include /* * For debugging, you can use the following macros. * remember, rdtsc() only works on Pentium-class machines quad_t ticks; DDB(ticks = rdtsc();) ... interesting code ... DDB(bdg_fw_ticks += (u_long)(rdtsc() - ticks) ; bdg_fw_count++ ;) * */ #define DDB(x) x #define DEB(x) static void bdginit(void *); static void flush_table(void); static void bdg_promisc_on(void); static void parse_bdg_cfg(void); static int bdg_initialized = 0; static int bdg_ipfw = 0 ; int do_bridge = 0; bdg_hash_table *bdg_table = NULL ; /* * System initialization */ SYSINIT(interfaces, SI_SUB_PROTO_IF, SI_ORDER_FIRST, bdginit, NULL) static struct bdg_stats bdg_stats ; struct bdg_softc *ifp2sc = NULL ; /* XXX make it static of size BDG_MAX_PORTS */ #define IFP_CHK(ifp, x) \ if (ifp2sc[ifp->if_index].magic != 0xDEADBEEF) { x ; } /* * turn off promisc mode, optionally clear the IFF_USED flag. * The flag is turned on by parse_bdg_config */ static void bdg_promisc_off(int clear_used) { struct ifnet *ifp ; - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next ) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link) ) { if ( (ifp2sc[ifp->if_index].flags & IFF_BDG_PROMISC) ) { int s, ret ; s = splimp(); ret = ifpromisc(ifp, 0); splx(s); ifp2sc[ifp->if_index].flags &= ~(IFF_BDG_PROMISC|IFF_MUTE) ; printf(">> now %s%d promisc OFF if_flags 0x%x bdg_flags 0x%x\n", ifp->if_name, ifp->if_unit, ifp->if_flags, ifp2sc[ifp->if_index].flags); } if (clear_used) { ifp2sc[ifp->if_index].flags &= ~(IFF_USED) ; bdg_stats.s[ifp->if_index].name[0] = '\0'; } } } /* * set promisc mode on the interfaces we use. */ static void bdg_promisc_on() { struct ifnet *ifp ; int s ; - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next ) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link) ) { if ( !BDG_USED(ifp) ) continue ; if ( 0 == ( ifp->if_flags & IFF_UP) ) { s = splimp(); if_up(ifp); splx(s); } if ( !(ifp2sc[ifp->if_index].flags & IFF_BDG_PROMISC) ) { int ret ; s = splimp(); ret = ifpromisc(ifp, 1); splx(s); ifp2sc[ifp->if_index].flags |= IFF_BDG_PROMISC ; printf(">> now %s%d promisc ON if_flags 0x%x bdg_flags 0x%x\n", ifp->if_name, ifp->if_unit, ifp->if_flags, ifp2sc[ifp->if_index].flags); } if (BDG_MUTED(ifp)) { printf(">> unmuting %s%d\n", ifp->if_name, ifp->if_unit); BDG_UNMUTE(ifp) ; } } } static int sysctl_bdg(SYSCTL_HANDLER_ARGS) { int error, oldval = do_bridge ; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); DEB( printf("called sysctl for bridge name %s arg2 %d val %d->%d\n", oidp->oid_name, oidp->oid_arg2, oldval, do_bridge); ) if (bdg_table == NULL) do_bridge = 0 ; if (oldval != do_bridge) { bdg_promisc_off( 1 ); /* reset previously used interfaces */ flush_table(); if (do_bridge) { parse_bdg_cfg(); bdg_promisc_on(); } } return error ; } static char bridge_cfg[256] = { "" } ; /* * parse the config string, set IFF_USED, name and cluster_id * for all interfaces found. */ static void parse_bdg_cfg() { char *p, *beg ; int i, l, cluster; struct bdg_softc *b; for (p= bridge_cfg; *p ; p++) { /* interface names begin with [a-z] and continue up to ':' */ if (*p < 'a' || *p > 'z') continue ; for ( beg = p ; *p && *p != ':' ; p++ ) ; if (*p == 0) /* end of string, ':' not found */ return ; l = p - beg ; /* length of name string */ p++ ; DDB(printf("-- match beg(%d) <%s> p <%s>\n", l, beg, p);) for (cluster = 0 ; *p && *p >= '0' && *p <= '9' ; p++) cluster = cluster*10 + (*p -'0'); /* * now search in bridge strings */ for (i=0, b = ifp2sc ; i < if_index ; i++, b++) { char buf[32]; struct ifnet *ifp = b->ifp ; if (ifp == NULL) continue; sprintf(buf, "%s%d", ifp->if_name, ifp->if_unit); if (!strncmp(beg, buf, l)) { /* XXX not correct for >10 if! */ b->cluster_id = htons(cluster) ; b->flags |= IFF_USED ; sprintf(bdg_stats.s[ifp->if_index].name, "%s%d:%d", ifp->if_name, ifp->if_unit, cluster); DDB(printf("--++ found %s\n", bdg_stats.s[ifp->if_index].name);) break ; } } if (*p == '\0') break ; } } static int sysctl_bdg_cfg(SYSCTL_HANDLER_ARGS) { int error = 0 ; char oldval[256] ; strcpy(oldval, bridge_cfg) ; error = sysctl_handle_string(oidp, bridge_cfg, oidp->oid_arg2, req); DEB( printf("called sysctl for bridge name %s arg2 %d err %d val %s->%s\n", oidp->oid_name, oidp->oid_arg2, error, oldval, bridge_cfg); ) if (strcmp(oldval, bridge_cfg)) { bdg_promisc_off( 1 ); /* reset previously-used interfaces */ flush_table(); parse_bdg_cfg(); /* and set new ones... */ if (do_bridge) bdg_promisc_on(); /* re-enable interfaces */ } return error ; } static int sysctl_refresh(SYSCTL_HANDLER_ARGS) { if (req->newptr) bdgtakeifaces(); return 0; } SYSCTL_DECL(_net_link_ether); SYSCTL_PROC(_net_link_ether, OID_AUTO, bridge_cfg, CTLTYPE_STRING|CTLFLAG_RW, &bridge_cfg, sizeof(bridge_cfg), &sysctl_bdg_cfg, "A", "Bridge configuration"); SYSCTL_PROC(_net_link_ether, OID_AUTO, bridge, CTLTYPE_INT|CTLFLAG_RW, &do_bridge, 0, &sysctl_bdg, "I", "Bridging"); SYSCTL_INT(_net_link_ether, OID_AUTO, bridge_ipfw, CTLFLAG_RW, &bdg_ipfw,0,"Pass bridged pkts through firewall"); #define SY(parent, var, comment) \ static int var ; \ SYSCTL_INT(parent, OID_AUTO, var, CTLFLAG_RW, &(var), 0, comment); int bdg_ipfw_drops; SYSCTL_INT(_net_link_ether, OID_AUTO, bridge_ipfw_drop, CTLFLAG_RW, &bdg_ipfw_drops,0,""); int bdg_ipfw_colls; SYSCTL_INT(_net_link_ether, OID_AUTO, bridge_ipfw_collisions, CTLFLAG_RW, &bdg_ipfw_colls,0,""); SYSCTL_PROC(_net_link_ether, OID_AUTO, bridge_refresh, CTLTYPE_INT|CTLFLAG_WR, NULL, 0, &sysctl_refresh, "I", "iface refresh"); #if 1 /* diagnostic vars */ SY(_net_link_ether, verbose, "Be verbose"); SY(_net_link_ether, bdg_split_pkts, "Packets split in bdg_forward"); SY(_net_link_ether, bdg_thru, "Packets through bridge"); SY(_net_link_ether, bdg_copied, "Packets copied in bdg_forward"); SY(_net_link_ether, bdg_copy, "Force copy in bdg_forward"); SY(_net_link_ether, bdg_predict, "Correctly predicted header location"); SY(_net_link_ether, bdg_fw_avg, "Cycle counter avg"); SY(_net_link_ether, bdg_fw_ticks, "Cycle counter item"); SY(_net_link_ether, bdg_fw_count, "Cycle counter count"); #endif SYSCTL_STRUCT(_net_link_ether, PF_BDG, bdgstats, CTLFLAG_RD, &bdg_stats , bdg_stats, "bridge statistics"); static int bdg_loops ; /* * completely flush the bridge table. */ static void flush_table() { int s,i; if (bdg_table == NULL) return ; s = splimp(); for (i=0; i< HASH_SIZE; i++) bdg_table[i].name= NULL; /* clear table */ splx(s); } /* * called periodically to flush entries etc. */ static void bdg_timeout(void *dummy) { static int slowtimer = 0 ; if (do_bridge) { static int age_index = 0 ; /* index of table position to age */ int l = age_index + HASH_SIZE/4 ; /* * age entries in the forwarding table. */ if (l > HASH_SIZE) l = HASH_SIZE ; for (; age_index < l ; age_index++) if (bdg_table[age_index].used) bdg_table[age_index].used = 0 ; else if (bdg_table[age_index].name) { /* printf("xx flushing stale entry %d\n", age_index); */ bdg_table[age_index].name = NULL ; } if (age_index >= HASH_SIZE) age_index = 0 ; if (--slowtimer <= 0 ) { slowtimer = 5 ; bdg_promisc_on() ; /* we just need unmute, really */ bdg_loops = 0 ; } } timeout(bdg_timeout, (void *)0, 2*hz ); } /* * local MAC addresses are held in a small array. This makes comparisons * much faster. */ bdg_addr bdg_addresses[BDG_MAX_PORTS]; int bdg_ports ; /* * initialization of bridge code. This needs to be done after all * interfaces have been configured. */ static void bdginit(void *dummy) { bdg_initialized++; if (bdg_table == NULL) bdg_table = (struct hash_table *) malloc(HASH_SIZE * sizeof(struct hash_table), M_IFADDR, M_WAITOK); flush_table(); ifp2sc = malloc(BDG_MAX_PORTS * sizeof(struct bdg_softc), M_IFADDR, M_WAITOK | M_ZERO); bzero(&bdg_stats, sizeof(bdg_stats) ); bdgtakeifaces(); bdg_timeout(0); do_bridge=0; } void bdgtakeifaces(void) { int i ; struct ifnet *ifp; struct arpcom *ac ; bdg_addr *p = bdg_addresses ; struct bdg_softc *bp; if (!bdg_initialized) return; bdg_ports = 0 ; *bridge_cfg = '\0'; printf("BRIDGE 010131, have %d interfaces\n", if_index); - for (i = 0 , ifp = ifnet.tqh_first ; i < if_index ; - i++, ifp = ifp->if_link.tqe_next) + for (i = 0 , ifp = TAILQ_FIRST(&ifnet) ; i < if_index ; + i++, ifp = TAILQ_NEXT(ifp, if_link)) if (ifp->if_type == IFT_ETHER) { /* ethernet ? */ bp = &ifp2sc[ifp->if_index] ; ac = (struct arpcom *)ifp; sprintf(bridge_cfg + strlen(bridge_cfg), "%s%d:1,", ifp->if_name, ifp->if_unit); printf("-- index %d %s type %d phy %d addrl %d addr %6D\n", ifp->if_index, bdg_stats.s[ifp->if_index].name, (int)ifp->if_type, (int) ifp->if_physical, (int)ifp->if_addrlen, ac->ac_enaddr, "." ); bcopy(ac->ac_enaddr, p->etheraddr, 6); p++ ; bp->ifp = ifp ; bp->flags = IFF_USED ; bp->cluster_id = htons(1) ; bp->magic = 0xDEADBEEF ; sprintf(bdg_stats.s[ifp->if_index].name, "%s%d:%d", ifp->if_name, ifp->if_unit, ntohs(bp->cluster_id)); bdg_ports ++ ; } } /* * bridge_in() is invoked to perform bridging decision on input packets. * * On Input: * eh Ethernet header of the incoming packet. * * On Return: destination of packet, one of * BDG_BCAST broadcast * BDG_MCAST multicast * BDG_LOCAL is only for a local address (do not forward) * BDG_DROP drop the packet * ifp ifp of the destination interface. * * Forwarding is not done directly to give a chance to some drivers * to fetch more of the packet, or simply drop it completely. */ struct ifnet * bridge_in(struct ifnet *ifp, struct ether_header *eh) { int index; struct ifnet *dst , *old ; int dropit = BDG_MUTED(ifp) ; /* * hash the source address */ index= HASH_FN(eh->ether_shost); bdg_table[index].used = 1 ; old = bdg_table[index].name ; if ( old ) { /* the entry is valid. */ IFP_CHK(old, printf("bridge_in-- reading table\n") ); if (!BDG_MATCH( eh->ether_shost, bdg_table[index].etheraddr) ) { bdg_ipfw_colls++ ; bdg_table[index].name = NULL ; } else if (old != ifp) { /* * found a loop. Either a machine has moved, or there * is a misconfiguration/reconfiguration of the network. * First, do not forward this packet! * Record the relocation anyways; then, if loops persist, * suspect a reconfiguration and disable forwarding * from the old interface. */ bdg_table[index].name = ifp ; /* relocate address */ printf("-- loop (%d) %6D to %s%d from %s%d (%s)\n", bdg_loops, eh->ether_shost, ".", ifp->if_name, ifp->if_unit, old->if_name, old->if_unit, BDG_MUTED(old) ? "muted":"active"); dropit = 1 ; if ( !BDG_MUTED(old) ) { if (++bdg_loops > 10) BDG_MUTE(old) ; } } } /* * now write the source address into the table */ if (bdg_table[index].name == NULL) { DEB(printf("new addr %6D at %d for %s%d\n", eh->ether_shost, ".", index, ifp->if_name, ifp->if_unit);) bcopy(eh->ether_shost, bdg_table[index].etheraddr, 6); bdg_table[index].name = ifp ; } dst = bridge_dst_lookup(eh); /* Return values: * BDG_BCAST, BDG_MCAST, BDG_LOCAL, BDG_UNKNOWN, BDG_DROP, ifp. * For muted interfaces, the first 3 are changed in BDG_LOCAL, * and others to BDG_DROP. Also, for incoming packets, ifp is changed * to BDG_DROP in case ifp == src . These mods are not necessary * for outgoing packets from ether_output(). */ BDG_STAT(ifp, BDG_IN); switch ((int)dst) { case (int)BDG_BCAST: case (int)BDG_MCAST: case (int)BDG_LOCAL: case (int)BDG_UNKNOWN: case (int)BDG_DROP: BDG_STAT(ifp, dst); break ; default : if (dst == ifp || dropit ) BDG_STAT(ifp, BDG_DROP); else BDG_STAT(ifp, BDG_FORWARD); break ; } if ( dropit ) { if (dst == BDG_BCAST || dst == BDG_MCAST || dst == BDG_LOCAL) return BDG_LOCAL ; else return BDG_DROP ; } else { return (dst == ifp ? BDG_DROP : dst ) ; } } /* * Forward to dst, excluding src port and muted interfaces. * The packet is freed if possible (i.e. surely not of interest for * the upper layer), otherwise a copy is left for use by the caller * (pointer in m0). * * It would be more efficient to make bdg_forward() always consume * the packet, leaving to the caller the task to check if it needs a copy * and get one in case. As it is now, bdg_forward() can sometimes make * a copy whereas it is not necessary. * * XXX be careful about eh, it can be a pointer into *m */ struct mbuf * bdg_forward(struct mbuf *m0, struct ether_header *const eh, struct ifnet *dst) { struct ifnet *src = m0->m_pkthdr.rcvif; /* could be NULL in output */ struct ifnet *ifp, *last = NULL ; int s ; int shared = bdg_copy ; /* someone else is using the mbuf */ int once = 0; /* loop only once */ /* * XXX eh is usually a pointer within the mbuf (some ethernet drivers * do that), so we better copy it before doing anything with the mbuf, * or we might corrupt the header. */ struct ether_header save_eh = *eh ; DEB(quad_t ticks; ticks = rdtsc();) bdg_thru++; if (dst == BDG_DROP) { /* this should not happen */ printf("xx bdg_forward for BDG_DROP\n"); #ifdef DUMMYNET if (m0->m_type == MT_DUMMYNET) /* XXX: Shouldn't have to be doing this. */ m_freem(m0->m_next); else #endif m_freem(m0); return NULL; } if (dst == BDG_LOCAL) { /* this should not happen as well */ printf("xx ouch, bdg_forward for local pkt\n"); return m0; } if (dst == BDG_BCAST || dst == BDG_MCAST || dst == BDG_UNKNOWN) { - ifp = ifnet.tqh_first ; /* scan all ports */ + ifp = TAILQ_FIRST(&ifnet) ; /* scan all ports */ once = 0 ; if (dst != BDG_UNKNOWN) /* need a copy for the local stack */ shared = 1 ; } else { ifp = dst ; once = 1 ; } if ( (u_int)(ifp) <= (u_int)BDG_FORWARD ) panic("bdg_forward: bad dst"); #ifdef IPFIREWALL /* * do filtering in a very similar way to what is done * in ip_output. Only for IP packets, and only pass/fail/dummynet * is supported. The tricky thing is to make sure that enough of * the packet (basically, Eth+IP+TCP/UDP headers) is contiguous * so that calls to m_pullup in ip_fw_chk will not kill the * ethernet header. */ if (ip_fw_chk_ptr) { struct ip_fw_chain *rule = NULL ; struct ip *ip ; int i; #ifdef DUMMYNET if (m0->m_type == MT_DUMMYNET) { /* * the packet was already tagged, so part of the * processing was already done, and we need to go down. */ rule = (struct ip_fw_chain *)(m0->m_data) ; m0 = m0->m_next ; src = m0->m_pkthdr.rcvif; /* could be NULL in output */ shared = 0 ; /* for sure, a copy is not needed later. */ bdg_thru--; /* already accounted for once */ goto forward; /* HACK! I should obey the fw_one_pass */ } #endif if (bdg_ipfw == 0) /* this test must be here. */ goto forward ; if (src == NULL) goto forward ; /* do not apply to packets from ether_output */ if (ntohs(save_eh.ether_type) != ETHERTYPE_IP) goto forward ; /* not an IP packet, ipfw is not appropriate */ if (m0->m_pkthdr.len < sizeof(struct ip) ) goto forward ; /* header too short for an IP pkt, cannot filter */ /* * i need some amt of data to be contiguous, and in case others need * the packet (shared==1) also better be in the first mbuf. */ i = min(m0->m_pkthdr.len, max_protohdr) ; if ( shared || m0->m_len < i) { m0 = m_pullup(m0, i) ; if (m0 == NULL) { printf("-- bdg: pullup failed.\n") ; return NULL ; } } /* * before calling the firewall, swap fields the same as IP does. * here we assume the pkt is an IP one and the header is contiguous */ ip = mtod(m0, struct ip *); NTOHS(ip->ip_len); NTOHS(ip->ip_off); /* * The third parameter to the firewall code is the dst. interface. * Since we apply checks only on input pkts we use NULL. * The firewall knows this is a bridged packet as the cookie ptr * is NULL. */ i = (*ip_fw_chk_ptr)(&ip, 0, NULL, NULL /* cookie */, &m0, &rule, NULL); if (i & IP_FW_PORT_DENY_FLAG) { /* XXX new interface - discard */ m_freem(m0); m0 = NULL ; } else if (m0 == NULL) { printf("firewall using old interface\n"); } if (m0 == NULL) { /* pkt discarded by firewall */ if (verbose) printf("pkt discarded by firewall\n"); return NULL ; } /* * If we get here, the firewall has passed the pkt, but the * mbuf pointer might have changed. Restore the fields NTOHS()'d. */ HTONS(ip->ip_len); HTONS(ip->ip_off); if (i == 0) /* a PASS rule. */ goto forward ; #ifdef DUMMYNET if (i & 0x10000) { /* * pass the pkt to dummynet, which consumes it. * If shared, make a copy and keep the origina. * Need to prepend the ethernet header, optimize the common * case of eh pointing already into the original mbuf. */ struct mbuf *m ; if (shared) { m = m_copypacket(m0, M_DONTWAIT); if (m == NULL) { printf("bdg_fwd: copy(1) failed\n"); return m0; } } else { m = m0 ; /* pass the original to dummynet */ m0 = NULL ; /* and nothing back to the caller */ } if ( (void *)(eh + 1) == (void *)m->m_data) { m->m_data -= ETHER_HDR_LEN ; m->m_len += ETHER_HDR_LEN ; m->m_pkthdr.len += ETHER_HDR_LEN ; bdg_predict++; } else { M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (!m && verbose) printf("M_PREPEND failed\n"); if (m == NULL) /* nope... */ return m0 ; bcopy(&save_eh, mtod(m, struct ether_header *), ETHER_HDR_LEN); } dummynet_io((i & 0xffff), DN_TO_BDG_FWD, m, dst, NULL, 0, rule, 0); return m0 ; } #endif /* * XXX add divert/forward actions... */ /* if none of the above matches, we have to drop the pkt */ bdg_ipfw_drops++ ; printf("bdg_forward: No rules match, so dropping packet!\n"); return m0 ; } forward: #endif /* IPFIREWALL */ /* * Again, bring up the headers in case of shared bufs to avoid * corruptions in the future. */ if ( shared ) { int i = min(m0->m_pkthdr.len, max_protohdr) ; m0 = m_pullup(m0, i) ; if (m0 == NULL) { printf("-- bdg: pullup2 failed.\n") ; return NULL ; } } for (;;) { if (last) { /* need to forward packet */ struct mbuf *m ; if (shared == 0 && once ) { /* no need to copy */ m = m0 ; m0 = NULL ; /* original is gone */ } else { m = m_copypacket(m0, M_DONTWAIT); if (m == NULL) { printf("bdg_forward: sorry, m_copypacket failed!\n"); return m0 ; /* the original is still there... */ } } /* * Last part of ether_output: add header, queue pkt and start * output if interface not yet active. Optimized for the * common case of eh pointing already into the mbuf */ if ( (void *)(eh + 1) == (void *)m->m_data) { m->m_data -= ETHER_HDR_LEN ; m->m_len += ETHER_HDR_LEN ; m->m_pkthdr.len += ETHER_HDR_LEN ; bdg_predict++; } else { M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (!m && verbose) printf("M_PREPEND failed\n"); if (m == NULL) return m0; bcopy(&save_eh, mtod(m, struct ether_header *), ETHER_HDR_LEN); } if (! IF_HANDOFF(&last->if_snd, m, last)) { #if 0 BDG_MUTE(last); /* should I also mute ? */ #endif } BDG_STAT(last, BDG_OUT); last = NULL ; if (once) break ; } if (ifp == NULL) break ; if (ifp != src && /* do not send to self */ BDG_USED(ifp) && /* if used for bridging */ ! _IF_QFULL(&ifp->if_snd) && (ifp->if_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) && BDG_SAMECLUSTER(ifp, src) && !BDG_MUTED(ifp) ) last = ifp ; - ifp = ifp->if_link.tqe_next ; + ifp = TAILQ_NEXT(ifp, if_link) ; if (ifp == NULL) once = 1 ; } DEB(bdg_fw_ticks += (u_long)(rdtsc() - ticks) ; bdg_fw_count++ ; if (bdg_fw_count != 0) bdg_fw_avg = bdg_fw_ticks/bdg_fw_count; ) return m0 ; } Index: head/sys/net/hostcache.c =================================================================== --- head/sys/net/hostcache.c (revision 71958) +++ head/sys/net/hostcache.c (revision 71959) @@ -1,249 +1,249 @@ /* * Copyright 1997 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include MALLOC_DEFINE(M_HOSTCACHE, "hostcache", "per-host cache structure"); static struct hctable hctable[AF_MAX]; static int hc_timeout_interval = 120; static int hc_maxidle = 1800; static int cmpsa(const struct sockaddr *sa1, const struct sockaddr *sa2); static void hc_timeout(void *xhct); static void maybe_bump_hash(struct hctable *hct); int hc_init(int af, struct hccallback *hccb, int init_nelem, int primes) { struct hctable *hct; struct hchead *heads; u_long nelem; hct = &hctable[af]; nelem = init_nelem; if (hct->hct_nentries) return 0; if (primes) { heads = phashinit(init_nelem, M_HOSTCACHE, &nelem); } else { int i; MALLOC(heads, struct hchead *, nelem * sizeof *heads, M_HOSTCACHE, M_WAITOK); for (i = 0; i < nelem; i++) { LIST_INIT(&heads[i]); } } hct->hct_heads = heads; hct->hct_nentries = nelem; hct->hct_primes = primes; timeout(hc_timeout, hct, hc_timeout_interval * hz); return 0; } struct hcentry * hc_get(struct sockaddr *sa) { u_long hash; struct hcentry *hc; struct hctable *hct; int s; hct = &hctable[sa->sa_family]; if (hct->hct_nentries == 0) return 0; hash = hct->hct_cb->hccb_hash(sa, hct->hct_nentries); - hc = hct->hct_heads[hash].lh_first; - for (; hc; hc = hc->hc_link.le_next) { + hc = hct->hct_heads[hash]LIST_FIRST(&); + for (; hc; hc = LIST_NEXT(hc, hc_link)) { if (cmpsa(hc->hc_host, sa) == 0) break; } if (hc == 0) return 0; s = splnet(); if (hc->hc_rt && (hc->hc_rt->rt_flags & RTF_UP) == 0) { RTFREE(hc->hc_rt); hc->hc_rt = 0; } if (hc->hc_rt == 0) { hc->hc_rt = rtalloc1(hc->hc_host, 1, 0); } hc_ref(hc); splx(s); /* XXX move to front of list? */ return hc; } void hc_ref(struct hcentry *hc) { int s = splnet(); if (hc->hc_refcnt++ == 0) { hc->hc_hct->hct_idle--; hc->hc_hct->hct_active++; } splx(s); } void hc_rele(struct hcentry *hc) { int s = splnet(); #ifdef DIAGNOSTIC printf("hc_rele: %p: negative refcnt!\n", (void *)hc); #endif hc->hc_refcnt--; if (hc->hc_refcnt == 0) { hc->hc_hct->hct_idle++; hc->hc_hct->hct_active--; hc->hc_idlesince = mono_time; /* XXX right one? */ } splx(s); } /* * The user is expected to initialize hc_host with the address and everything * else to the appropriate form of `0'. */ int hc_insert(struct hcentry *hc) { struct hcentry *hc2; struct hctable *hct; u_long hash; int s; hct = &hctable[hc->hc_host->sa_family]; hash = hct->hct_cb->hccb_hash(hc->hc_host, hct->hct_nentries); - hc2 = hct->hct_heads[hash].lh_first; - for (; hc2; hc2 = hc2->hc_link.le_next) { + hc2 = hct->hct_heads[hash]LIST_FIRST(&); + for (; hc2; hc2 = LIST_NEXT(hc2, hc_link)) { if (cmpsa(hc2->hc_host, hc->hc_host) == 0) break; } if (hc2 != 0) return EEXIST; hc->hc_hct = hct; s = splnet(); LIST_INSERT_HEAD(&hct->hct_heads[hash], hc, hc_link); hct->hct_idle++; /* * If the table is now more than 75% full, consider bumping it. */ if (100 * (hct->hct_idle + hct->hct_active) > 75 * hct->hct_nentries) maybe_bump_hash(hct); splx(s); return 0; } /* * It's not clear to me how much sense this makes as an external interface, * since it is expected that the deletion will normally be handled by * the cache timeout. */ int hc_delete(struct hcentry *hc) { struct hctable *hct; int error, s; if (hc->hc_refcnt > 0) return 0; hct = hc->hc_hct; error = hct->hct_cb->hccb_delete(hc); if (error) return 0; s = splnet(); LIST_REMOVE(hc, hc_link); hc->hc_hct->hct_idle--; splx(s); FREE(hc, M_HOSTCACHE); return 0; } static void hc_timeout(void *xhct) { struct hcentry *hc; struct hctable *hct; int j, s; time_t start; hct = xhct; start = mono_time.tv_sec; /* for simplicity */ if (hct->hct_idle == 0) return; for (j = 0; j < hct->hct_nentries; j++) { - for (hc = hct->hct_heads[j].lh_first; hc; - hc = hc->hc_link.le_next) { + for (hc = hct->hct_heads[j]LIST_FIRST(&); hc; + hc = LIST_NEXT(hc, hc_link)) { if (hc->hc_refcnt > 0) continue; if (hc->hc_idlesince.tv_sec + hc_maxidle <= start) { if (hct->hct_cb->hccb_delete(hc)) continue; s = splnet(); LIST_REMOVE(hc, hc_link); hct->hct_idle--; splx(s); } } } /* * Fiddle something here based on tot_idle... */ timeout(hc_timeout, xhct, hc_timeout_interval * hz); } static int cmpsa(const struct sockaddr *sa1, const struct sockaddr *sa2) { if (sa1->sa_len != sa2->sa_len) return ((int)sa1->sa_len - sa2->sa_len); return bcmp(sa1, sa2, sa1->sa_len); } static void maybe_bump_hash(struct hctable *hct) { ; /* XXX fill me in */ } Index: head/sys/net/if.c =================================================================== --- head/sys/net/if.c (revision 71958) +++ head/sys/net/if.c (revision 71959) @@ -1,1394 +1,1394 @@ /* * Copyright (c) 1980, 1986, 1993 * The 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. * * @(#)if.c 8.3 (Berkeley) 1/4/94 * $FreeBSD$ */ #include "opt_compat.h" #include "opt_inet6.h" #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) /*XXX*/ #include #include #ifdef INET6 #include #include #endif #endif /* * System initialization */ static int ifconf __P((u_long, caddr_t)); static void ifinit __P((void *)); static void if_qflush __P((struct ifqueue *)); static void if_slowtimo __P((void *)); static void link_rtrequest __P((int, struct rtentry *, struct sockaddr *)); static int if_rtdel __P((struct radix_node *, void *)); SYSINIT(interfaces, SI_SUB_PROTO_IF, SI_ORDER_FIRST, ifinit, NULL) MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); int ifqmaxlen = IFQ_MAXLEN; struct ifnethead ifnet; /* depend on static init XXX */ #ifdef INET6 /* * XXX: declare here to avoid to include many inet6 related files.. * should be more generalized? */ extern void nd6_setmtu __P((struct ifnet *)); #endif /* * Network interface utility routines. * * Routines with ifa_ifwith* names take sockaddr *'s as * parameters. */ /* ARGSUSED*/ void ifinit(dummy) void *dummy; { struct ifnet *ifp; int s; s = splimp(); - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) { if (ifp->if_snd.ifq_maxlen == 0) { printf("%s%d XXX: driver didn't set ifq_maxlen\n", ifp->if_name, ifp->if_unit); ifp->if_snd.ifq_maxlen = ifqmaxlen; } /* XXX This is an access violation of the mutex internals. */ if (ifp->if_snd.ifq_mtx.mtx_description == NULL) { printf("%s%d XXX: driver didn't initialize queue mtx\n", ifp->if_name, ifp->if_unit); mtx_init(&ifp->if_snd.ifq_mtx, "unknown", MTX_DEF); } } splx(s); if_slowtimo(0); } int if_index = 0; struct ifaddr **ifnet_addrs; struct ifnet **ifindex2ifnet = NULL; /* * Attach an interface to the * list of "active" interfaces. */ void if_attach(ifp) struct ifnet *ifp; { unsigned socksize, ifasize; int namelen, masklen; char workbuf[64]; register struct sockaddr_dl *sdl; register struct ifaddr *ifa; static int if_indexlim = 8; static int inited; if (!inited) { TAILQ_INIT(&ifnet); inited = 1; } TAILQ_INSERT_TAIL(&ifnet, ifp, if_link); ifp->if_index = ++if_index; /* * XXX - * The old code would work if the interface passed a pre-existing * chain of ifaddrs to this code. We don't trust our callers to * properly initialize the tailq, however, so we no longer allow * this unlikely case. */ TAILQ_INIT(&ifp->if_addrhead); TAILQ_INIT(&ifp->if_prefixhead); LIST_INIT(&ifp->if_multiaddrs); getmicrotime(&ifp->if_lastchange); if (ifnet_addrs == 0 || if_index >= if_indexlim) { unsigned n = (if_indexlim <<= 1) * sizeof(ifa); caddr_t q = malloc(n, M_IFADDR, M_WAITOK | M_ZERO); if (ifnet_addrs) { bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2); free((caddr_t)ifnet_addrs, M_IFADDR); } ifnet_addrs = (struct ifaddr **)q; /* grow ifindex2ifnet */ n = if_indexlim * sizeof(struct ifnet *); q = malloc(n, M_IFADDR, M_WAITOK | M_ZERO); if (ifindex2ifnet) { bcopy((caddr_t)ifindex2ifnet, q, n/2); free((caddr_t)ifindex2ifnet, M_IFADDR); } ifindex2ifnet = (struct ifnet **)q; } ifindex2ifnet[if_index] = ifp; mtx_init(&ifp->if_snd.ifq_mtx, ifp->if_name, MTX_DEF); /* * create a Link Level name for this device */ namelen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); #define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m)) masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen; socksize = masklen + ifp->if_addrlen; #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1))) if (socksize < sizeof(*sdl)) socksize = sizeof(*sdl); socksize = ROUNDUP(socksize); ifasize = sizeof(*ifa) + 2 * socksize; ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK | M_ZERO); if (ifa) { sdl = (struct sockaddr_dl *)(ifa + 1); sdl->sdl_len = socksize; sdl->sdl_family = AF_LINK; bcopy(workbuf, sdl->sdl_data, namelen); sdl->sdl_nlen = namelen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; ifnet_addrs[if_index - 1] = ifa; ifa->ifa_ifp = ifp; ifa->ifa_rtrequest = link_rtrequest; ifa->ifa_addr = (struct sockaddr *)sdl; sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl); ifa->ifa_netmask = (struct sockaddr *)sdl; sdl->sdl_len = masklen; while (namelen != 0) sdl->sdl_data[--namelen] = 0xff; TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); } } /* * Detach an interface, removing it from the * list of "active" interfaces. */ void if_detach(ifp) struct ifnet *ifp; { struct ifaddr *ifa; struct radix_node_head *rnh; int s; int i; /* * Remove routes and flush queues. */ s = splnet(); if_down(ifp); /* * Remove address from ifnet_addrs[] and maybe decrement if_index. * Clean up all addresses. */ ifnet_addrs[ifp->if_index - 1] = 0; while (if_index > 0 && ifnet_addrs[if_index - 1] == 0) if_index--; for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; ifa = TAILQ_FIRST(&ifp->if_addrhead)) { #ifdef INET /* XXX: Ugly!! ad hoc just for INET */ if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { struct ifaliasreq ifr; bzero(&ifr, sizeof(ifr)); ifr.ifra_addr = *ifa->ifa_addr; if (ifa->ifa_dstaddr) ifr.ifra_broadaddr = *ifa->ifa_dstaddr; if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp, NULL) == 0) continue; } #endif /* INET */ #ifdef INET6 if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { in6_purgeaddr(ifa, ifp); /* ifp_addrhead is already updated */ continue; } #endif /* INET6 */ TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); IFAFREE(ifa); } /* * Delete all remaining routes using this interface * Unfortuneatly the only way to do this is to slog through * the entire routing table looking for routes which point * to this interface...oh well... */ for (i = 1; i <= AF_MAX; i++) { if ((rnh = rt_tables[i]) == NULL) continue; (void) rnh->rnh_walktree(rnh, if_rtdel, ifp); } #ifdef INET6 /* nuke all IPv6 kernel structs related to ifp */ in6_ifdetach(ifp); #endif TAILQ_REMOVE(&ifnet, ifp, if_link); mtx_destroy(&ifp->if_snd.ifq_mtx); splx(s); } /* * Delete Routes for a Network Interface * * Called for each routing entry via the rnh->rnh_walktree() call above * to delete all route entries referencing a detaching network interface. * * Arguments: * rn pointer to node in the routing table * arg argument passed to rnh->rnh_walktree() - detaching interface * * Returns: * 0 successful * errno failed - reason indicated * */ static int if_rtdel(rn, arg) struct radix_node *rn; void *arg; { struct rtentry *rt = (struct rtentry *)rn; struct ifnet *ifp = arg; int err; if (rt->rt_ifp == ifp) { /* * Protect (sorta) against walktree recursion problems * with cloned routes */ if ((rt->rt_flags & RTF_UP) == 0) return (0); err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, (struct rtentry **) NULL); if (err) { log(LOG_WARNING, "if_rtdel: error %d\n", err); } } return (0); } /* * Locate an interface based on a complete address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithaddr(addr) register struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; #define equal(a1, a2) \ (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (equal(addr, ifa->ifa_addr)) return (ifa); if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && /* IP6 doesn't have broadcast */ ifa->ifa_broadaddr->sa_len != 0 && equal(ifa->ifa_broadaddr, addr)) return (ifa); } return ((struct ifaddr *)0); } /* * Locate the point to point interface with a given destination address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithdstaddr(addr) register struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) if (ifp->if_flags & IFF_POINTOPOINT) - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)) return (ifa); } return ((struct ifaddr *)0); } /* * Find an interface on a specific network. If many, choice * is most specific found. */ struct ifaddr * ifa_ifwithnet(addr) struct sockaddr *addr; { register struct ifnet *ifp; register struct ifaddr *ifa; struct ifaddr *ifa_maybe = (struct ifaddr *) 0; u_int af = addr->sa_family; char *addr_data = addr->sa_data, *cplim; /* * AF_LINK addresses can be looked up directly by their index number, * so do that if we can. */ if (af == AF_LINK) { register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr; if (sdl->sdl_index && sdl->sdl_index <= if_index) return (ifnet_addrs[sdl->sdl_index - 1]); } /* * Scan though each interface, looking for ones that have * addresses in this address family. */ - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) { + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) { register char *cp, *cp2, *cp3; if (ifa->ifa_addr->sa_family != af) next: continue; if ( #ifdef INET6 /* XXX: for maching gif tunnel dst as routing entry gateway */ addr->sa_family != AF_INET6 && #endif ifp->if_flags & IFF_POINTOPOINT) { /* * This is a bit broken as it doesn't * take into account that the remote end may * be a single node in the network we are * looking for. * The trouble is that we don't know the * netmask for the remote end. */ if (ifa->ifa_dstaddr != 0 && equal(addr, ifa->ifa_dstaddr)) return (ifa); } else { /* * if we have a special address handler, * then use it instead of the generic one. */ if (ifa->ifa_claim_addr) { if ((*ifa->ifa_claim_addr)(ifa, addr)) { return (ifa); } else { continue; } } /* * Scan all the bits in the ifa's address. * If a bit dissagrees with what we are * looking for, mask it with the netmask * to see if it really matters. * (A byte at a time) */ if (ifa->ifa_netmask == 0) continue; cp = addr_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; while (cp3 < cplim) if ((*cp++ ^ *cp2++) & *cp3++) goto next; /* next address! */ /* * If the netmask of what we just found * is more specific than what we had before * (if we had one) then remember the new one * before continuing to search * for an even better one. */ if (ifa_maybe == 0 || rn_refines((caddr_t)ifa->ifa_netmask, (caddr_t)ifa_maybe->ifa_netmask)) ifa_maybe = ifa; } } } return (ifa_maybe); } /* * Find an interface address specific to an interface best matching * a given address. */ struct ifaddr * ifaof_ifpforaddr(addr, ifp) struct sockaddr *addr; register struct ifnet *ifp; { register struct ifaddr *ifa; register char *cp, *cp2, *cp3; register char *cplim; struct ifaddr *ifa_maybe = 0; u_int af = addr->sa_family; if (af >= AF_MAX) return (0); - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) { if (ifa->ifa_addr->sa_family != af) continue; if (ifa_maybe == 0) ifa_maybe = ifa; if (ifa->ifa_netmask == 0) { if (equal(addr, ifa->ifa_addr) || (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))) return (ifa); continue; } if (ifp->if_flags & IFF_POINTOPOINT) { if (equal(addr, ifa->ifa_dstaddr)) return (ifa); } else { cp = addr->sa_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; for (; cp3 < cplim; cp3++) if ((*cp++ ^ *cp2++) & *cp3) break; if (cp3 == cplim) return (ifa); } } return (ifa_maybe); } #include /* * Default action when installing a route with a Link Level gateway. * Lookup an appropriate real ifa to point to. * This should be moved to /sys/net/link.c eventually. */ static void link_rtrequest(cmd, rt, sa) int cmd; register struct rtentry *rt; struct sockaddr *sa; { register struct ifaddr *ifa; struct sockaddr *dst; struct ifnet *ifp; if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) return; ifa = ifaof_ifpforaddr(dst, ifp); if (ifa) { IFAFREE(rt->rt_ifa); rt->rt_ifa = ifa; ifa->ifa_refcnt++; if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) ifa->ifa_rtrequest(cmd, rt, sa); } } /* * Mark an interface down and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_unroute(ifp, flag, fam) register struct ifnet *ifp; int flag, fam; { register struct ifaddr *ifa; ifp->if_flags &= ~flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFDOWN, ifa->ifa_addr); if_qflush(&ifp->if_snd); rt_ifmsg(ifp); } /* * Mark an interface up and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_route(ifp, flag, fam) register struct ifnet *ifp; int flag, fam; { register struct ifaddr *ifa; ifp->if_flags |= flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFUP, ifa->ifa_addr); rt_ifmsg(ifp); #ifdef INET6 in6_if_up(ifp); #endif } /* * Mark an interface down and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_down(ifp) register struct ifnet *ifp; { if_unroute(ifp, IFF_UP, AF_UNSPEC); } /* * Mark an interface up and notify protocols of * the transition. * NOTE: must be called at splnet or eqivalent. */ void if_up(ifp) register struct ifnet *ifp; { if_route(ifp, IFF_UP, AF_UNSPEC); } /* * Flush an interface queue. */ static void if_qflush(ifq) register struct ifqueue *ifq; { register struct mbuf *m, *n; n = ifq->ifq_head; while ((m = n) != 0) { n = m->m_act; m_freem(m); } ifq->ifq_head = 0; ifq->ifq_tail = 0; ifq->ifq_len = 0; } /* * Handle interface watchdog timer routines. Called * from softclock, we decrement timers (if set) and * call the appropriate interface routine on expiration. */ static void if_slowtimo(arg) void *arg; { register struct ifnet *ifp; int s = splimp(); - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) { if (ifp->if_timer == 0 || --ifp->if_timer) continue; if (ifp->if_watchdog) (*ifp->if_watchdog)(ifp); } splx(s); timeout(if_slowtimo, (void *)0, hz / IFNET_SLOWHZ); } /* * Map interface name to * interface structure pointer. */ struct ifnet * ifunit(char *name) { char namebuf[IFNAMSIZ + 1]; char *cp; struct ifnet *ifp; int unit; unsigned len, m; char c; len = strlen(name); if (len < 2 || len > IFNAMSIZ) return NULL; cp = name + len - 1; c = *cp; if (c < '0' || c > '9') return NULL; /* trailing garbage */ unit = 0; m = 1; do { if (cp == name) return NULL; /* no interface name */ unit += (c - '0') * m; if (unit > 1000000) return NULL; /* number is unreasonable */ m *= 10; c = *--cp; } while (c >= '0' && c <= '9'); len = cp - name + 1; bcopy(name, namebuf, len); namebuf[len] = '\0'; /* * Now search all the interfaces for this name/number */ - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) { if (strcmp(ifp->if_name, namebuf)) continue; if (unit == ifp->if_unit) break; } return (ifp); } /* * Map interface name in a sockaddr_dl to * interface structure pointer. */ struct ifnet * if_withname(sa) struct sockaddr *sa; { char ifname[IFNAMSIZ+1]; struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) || (sdl->sdl_nlen > IFNAMSIZ) ) return NULL; /* * ifunit wants a null-terminated name. It may not be null-terminated * in the sockaddr. We don't want to change the caller's sockaddr, * and there might not be room to put the trailing null anyway, so we * make a local copy that we know we can null terminate safely. */ bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen); ifname[sdl->sdl_nlen] = '\0'; return ifunit(ifname); } /* * Interface ioctls. */ int ifioctl(so, cmd, data, p) struct socket *so; u_long cmd; caddr_t data; struct proc *p; { register struct ifnet *ifp; register struct ifreq *ifr; struct ifstat *ifs; int error; short oif_flags; switch (cmd) { case SIOCGIFCONF: case OSIOCGIFCONF: return (ifconf(cmd, data)); } ifr = (struct ifreq *)data; ifp = ifunit(ifr->ifr_name); if (ifp == 0) return (ENXIO); switch (cmd) { case SIOCGIFFLAGS: ifr->ifr_flags = ifp->if_flags; break; case SIOCGIFMETRIC: ifr->ifr_metric = ifp->if_metric; break; case SIOCGIFMTU: ifr->ifr_mtu = ifp->if_mtu; break; case SIOCGIFPHYS: ifr->ifr_phys = ifp->if_physical; break; case SIOCSIFFLAGS: error = suser(p); if (error) return (error); ifr->ifr_prevflags = ifp->if_flags; if (ifp->if_flags & IFF_SMART) { /* Smart drivers twiddle their own routes */ } else if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) { int s = splimp(); if_down(ifp); splx(s); } else if (ifr->ifr_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) { int s = splimp(); if_up(ifp); splx(s); } ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | (ifr->ifr_flags &~ IFF_CANTCHANGE); if (ifp->if_ioctl) (void) (*ifp->if_ioctl)(ifp, cmd, data); getmicrotime(&ifp->if_lastchange); break; case SIOCSIFMETRIC: error = suser(p); if (error) return (error); ifp->if_metric = ifr->ifr_metric; getmicrotime(&ifp->if_lastchange); break; case SIOCSIFPHYS: error = suser(p); if (error) return error; if (!ifp->if_ioctl) return EOPNOTSUPP; error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); return(error); case SIOCSIFMTU: { u_long oldmtu = ifp->if_mtu; error = suser(p); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) return (EINVAL); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) { getmicrotime(&ifp->if_lastchange); rt_ifmsg(ifp); } /* * If the link MTU changed, do network layer specific procedure. */ if (ifp->if_mtu != oldmtu) { #ifdef INET6 nd6_setmtu(ifp); #endif } return (error); } case SIOCADDMULTI: case SIOCDELMULTI: error = suser(p); if (error) return (error); /* Don't allow group membership on non-multicast interfaces. */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return EOPNOTSUPP; /* Don't let users screw up protocols' entries. */ if (ifr->ifr_addr.sa_family != AF_LINK) return EINVAL; if (cmd == SIOCADDMULTI) { struct ifmultiaddr *ifma; error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); } else { error = if_delmulti(ifp, &ifr->ifr_addr); } if (error == 0) getmicrotime(&ifp->if_lastchange); return error; case SIOCSIFPHYADDR: case SIOCDIFPHYADDR: #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif case SIOCSIFMEDIA: case SIOCSIFGENERIC: error = suser(p); if (error) return (error); if (ifp->if_ioctl == 0) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); return error; case SIOCGIFSTATUS: ifs = (struct ifstat *)data; ifs->ascii[0] = '\0'; case SIOCGIFMEDIA: case SIOCGIFGENERIC: if (ifp->if_ioctl == 0) return (EOPNOTSUPP); return ((*ifp->if_ioctl)(ifp, cmd, data)); case SIOCSIFLLADDR: error = suser(p); if (error) return (error); return if_setlladdr(ifp, ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len); default: oif_flags = ifp->if_flags; if (so->so_proto == 0) return (EOPNOTSUPP); #ifndef COMPAT_43 error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, p)); #else { int ocmd = cmd; switch (cmd) { case SIOCSIFDSTADDR: case SIOCSIFADDR: case SIOCSIFBRDADDR: case SIOCSIFNETMASK: #if BYTE_ORDER != BIG_ENDIAN if (ifr->ifr_addr.sa_family == 0 && ifr->ifr_addr.sa_len < 16) { ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len; ifr->ifr_addr.sa_len = 16; } #else if (ifr->ifr_addr.sa_len == 0) ifr->ifr_addr.sa_len = 16; #endif break; case OSIOCGIFADDR: cmd = SIOCGIFADDR; break; case OSIOCGIFDSTADDR: cmd = SIOCGIFDSTADDR; break; case OSIOCGIFBRDADDR: cmd = SIOCGIFBRDADDR; break; case OSIOCGIFNETMASK: cmd = SIOCGIFNETMASK; } error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, p)); switch (ocmd) { case OSIOCGIFADDR: case OSIOCGIFDSTADDR: case OSIOCGIFBRDADDR: case OSIOCGIFNETMASK: *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family; } } #endif /* COMPAT_43 */ if ((oif_flags ^ ifp->if_flags) & IFF_UP) { #ifdef INET6 DELAY(100);/* XXX: temporal workaround for fxp issue*/ if (ifp->if_flags & IFF_UP) { int s = splimp(); in6_if_up(ifp); splx(s); } #endif } return (error); } return (0); } /* * Set/clear promiscuous mode on interface ifp based on the truth value * of pswitch. The calls are reference counted so that only the first * "on" request actually has an effect, as does the final "off" request. * Results are undefined if the "off" and "on" requests are not matched. */ int ifpromisc(ifp, pswitch) struct ifnet *ifp; int pswitch; { struct ifreq ifr; int error; if (pswitch) { /* * If the device is not configured up, we cannot put it in * promiscuous mode. */ if ((ifp->if_flags & IFF_UP) == 0) return (ENETDOWN); if (ifp->if_pcount++ != 0) return (0); ifp->if_flags |= IFF_PROMISC; log(LOG_INFO, "%s%d: promiscuous mode enabled\n", ifp->if_name, ifp->if_unit); } else { if (--ifp->if_pcount > 0) return (0); ifp->if_flags &= ~IFF_PROMISC; log(LOG_INFO, "%s%d: promiscuous mode disabled\n", ifp->if_name, ifp->if_unit); } ifr.ifr_flags = ifp->if_flags; error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); if (error == 0) rt_ifmsg(ifp); return error; } /* * Return interface configuration * of system. List may be used * in later ioctl's (above) to get * other information. */ /*ARGSUSED*/ static int ifconf(cmd, data) u_long cmd; caddr_t data; { register struct ifconf *ifc = (struct ifconf *)data; - register struct ifnet *ifp = ifnet.tqh_first; + register struct ifnet *ifp = TAILQ_FIRST(&ifnet); register struct ifaddr *ifa; struct ifreq ifr, *ifrp; int space = ifc->ifc_len, error = 0; ifrp = ifc->ifc_req; - for (; space > sizeof (ifr) && ifp; ifp = ifp->if_link.tqe_next) { + for (; space > sizeof (ifr) && ifp; ifp = TAILQ_NEXT(ifp, if_link)) { char workbuf[64]; int ifnlen, addrs; ifnlen = snprintf(workbuf, sizeof(workbuf), "%s%d", ifp->if_name, ifp->if_unit); if(ifnlen + 1 > sizeof ifr.ifr_name) { error = ENAMETOOLONG; break; } else { strcpy(ifr.ifr_name, workbuf); } addrs = 0; - ifa = ifp->if_addrhead.tqh_first; + ifa = TAILQ_FIRST(&ifp->if_addrhead); for ( ; space > sizeof (ifr) && ifa; - ifa = ifa->ifa_link.tqe_next) { + ifa = TAILQ_NEXT(ifa, ifa_link)) { register struct sockaddr *sa = ifa->ifa_addr; if (curproc->p_prison && prison_if(curproc, sa)) continue; addrs++; #ifdef COMPAT_43 if (cmd == OSIOCGIFCONF) { struct osockaddr *osa = (struct osockaddr *)&ifr.ifr_addr; ifr.ifr_addr = *sa; osa->sa_family = sa->sa_family; error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); ifrp++; } else #endif if (sa->sa_len <= sizeof(*sa)) { ifr.ifr_addr = *sa; error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); ifrp++; } else { if (space < sizeof (ifr) + sa->sa_len - sizeof(*sa)) break; space -= sa->sa_len - sizeof(*sa); error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr.ifr_name)); if (error == 0) error = copyout((caddr_t)sa, (caddr_t)&ifrp->ifr_addr, sa->sa_len); ifrp = (struct ifreq *) (sa->sa_len + (caddr_t)&ifrp->ifr_addr); } if (error) break; space -= sizeof (ifr); } if (error) break; if (!addrs) { bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); if (error) break; space -= sizeof (ifr); ifrp++; } } ifc->ifc_len -= space; return (error); } /* * Just like if_promisc(), but for all-multicast-reception mode. */ int if_allmulti(ifp, onswitch) struct ifnet *ifp; int onswitch; { int error = 0; int s = splimp(); if (onswitch) { if (ifp->if_amcount++ == 0) { ifp->if_flags |= IFF_ALLMULTI; error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0); } } else { if (ifp->if_amcount > 1) { ifp->if_amcount--; } else { ifp->if_amcount = 0; ifp->if_flags &= ~IFF_ALLMULTI; error = ifp->if_ioctl(ifp, SIOCSIFFLAGS, 0); } } splx(s); if (error == 0) rt_ifmsg(ifp); return error; } /* * Add a multicast listenership to the interface in question. * The link layer provides a routine which converts */ int if_addmulti(ifp, sa, retifma) struct ifnet *ifp; /* interface to manipulate */ struct sockaddr *sa; /* address to add */ struct ifmultiaddr **retifma; { struct sockaddr *llsa, *dupsa; int error, s; struct ifmultiaddr *ifma; /* * If the matching multicast address already exists * then don't add a new one, just add a reference */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) { + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma; + ifma = LIST_NEXT(ifma, ifma_link)) { if (equal(sa, ifma->ifma_addr)) { ifma->ifma_refcount++; if (retifma) *retifma = ifma; return 0; } } /* * Give the link layer a chance to accept/reject it, and also * find out which AF_LINK address this maps to, if it isn't one * already. */ if (ifp->if_resolvemulti) { error = ifp->if_resolvemulti(ifp, &llsa, sa); if (error) return error; } else { llsa = 0; } MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); MALLOC(dupsa, struct sockaddr *, sa->sa_len, M_IFMADDR, M_WAITOK); bcopy(sa, dupsa, sa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_lladdr = llsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; ifma->ifma_protospec = 0; rt_newmaddrmsg(RTM_NEWMADDR, ifma); /* * Some network interfaces can scan the address list at * interrupt time; lock them out. */ s = splimp(); LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); splx(s); *retifma = ifma; if (llsa != 0) { - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) { + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma; + ifma = LIST_NEXT(ifma, ifma_link)) { if (equal(ifma->ifma_addr, llsa)) break; } if (ifma) { ifma->ifma_refcount++; } else { MALLOC(ifma, struct ifmultiaddr *, sizeof *ifma, M_IFMADDR, M_WAITOK); MALLOC(dupsa, struct sockaddr *, llsa->sa_len, M_IFMADDR, M_WAITOK); bcopy(llsa, dupsa, llsa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; s = splimp(); LIST_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); splx(s); } } /* * We are certain we have added something, so call down to the * interface to let them know about it. */ s = splimp(); ifp->if_ioctl(ifp, SIOCADDMULTI, 0); splx(s); return 0; } /* * Remove a reference to a multicast address on this interface. Yell * if the request does not match an existing membership. */ int if_delmulti(ifp, sa) struct ifnet *ifp; struct sockaddr *sa; { struct ifmultiaddr *ifma; int s; - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma; + ifma = LIST_NEXT(ifma, ifma_link)) if (equal(sa, ifma->ifma_addr)) break; if (ifma == 0) return ENOENT; if (ifma->ifma_refcount > 1) { ifma->ifma_refcount--; return 0; } rt_newmaddrmsg(RTM_DELMADDR, ifma); sa = ifma->ifma_lladdr; s = splimp(); LIST_REMOVE(ifma, ifma_link); splx(s); free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); if (sa == 0) return 0; /* * Now look for the link-layer address which corresponds to * this network address. It had been squirreled away in * ifma->ifma_lladdr for this purpose (so we don't have * to call ifp->if_resolvemulti() again), and we saved that * value in sa above. If some nasty deleted the * link-layer address out from underneath us, we can deal because * the address we stored was is not the same as the one which was * in the record for the link-layer address. (So we don't complain * in that case.) */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma; + ifma = LIST_NEXT(ifma, ifma_link)) if (equal(sa, ifma->ifma_addr)) break; if (ifma == 0) return 0; if (ifma->ifma_refcount > 1) { ifma->ifma_refcount--; return 0; } s = splimp(); LIST_REMOVE(ifma, ifma_link); ifp->if_ioctl(ifp, SIOCDELMULTI, 0); splx(s); free(ifma->ifma_addr, M_IFMADDR); free(sa, M_IFMADDR); free(ifma, M_IFMADDR); return 0; } /* * Set the link layer address on an interface. * * At this time we only support certain types of interfaces, * and we don't allow the length of the address to change. */ int if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) { struct sockaddr_dl *sdl; struct ifaddr *ifa; ifa = ifnet_addrs[ifp->if_index - 1]; if (ifa == NULL) return (EINVAL); sdl = (struct sockaddr_dl *)ifa->ifa_addr; if (sdl == NULL) return (EINVAL); if (len != sdl->sdl_alen) /* don't allow length to change */ return (EINVAL); switch (ifp->if_type) { case IFT_ETHER: /* these types use struct arpcom */ case IFT_FDDI: case IFT_XETHER: case IFT_ISO88025: case IFT_PROPVIRTUAL: /* XXX waiting for IFT_8021_VLAN */ bcopy(lladdr, ((struct arpcom *)ifp->if_softc)->ac_enaddr, len); bcopy(lladdr, LLADDR(sdl), len); break; default: return (ENODEV); } /* * If the interface is already up, we need * to re-init it in order to reprogram its * address filter. */ if ((ifp->if_flags & IFF_UP) != 0) { ifp->if_flags &= ~IFF_UP; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL); ifp->if_flags |= IFF_UP; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL); } return (0); } struct ifmultiaddr * ifmaof_ifpforaddr(sa, ifp) struct sockaddr *sa; struct ifnet *ifp; { struct ifmultiaddr *ifma; - for (ifma = ifp->if_multiaddrs.lh_first; ifma; - ifma = ifma->ifma_link.le_next) + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma; + ifma = LIST_NEXT(ifma, ifma_link)) if (equal(ifma->ifma_addr, sa)) break; return ifma; } SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers"); SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management"); Index: head/sys/net/if_atmsubr.c =================================================================== --- head/sys/net/if_atmsubr.c (revision 71958) +++ head/sys/net/if_atmsubr.c (revision 71959) @@ -1,334 +1,334 @@ /* $NetBSD: if_atmsubr.c,v 1.10 1997/03/11 23:19:51 chuck Exp $ */ /* * * Copyright (c) 1996 Charles D. Cranor and Washington University. * 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 Charles D. Cranor and * Washington University. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * $FreeBSD$ */ /* * if_atmsubr.c */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_natm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX: for ETHERTYPE_* */ #if defined(INET) || defined(INET6) #include #endif #ifdef NATM #include #endif #ifndef ETHERTYPE_IPV6 #define ETHERTYPE_IPV6 0x86dd #endif #define senderr(e) { error = (e); goto bad;} /* * atm_output: ATM output routine * inputs: * "ifp" = ATM interface to output to * "m0" = the packet to output * "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI) * "rt0" = the route to use * returns: error code [0 == ok] * * note: special semantic: if (dst == NULL) then we assume "m" already * has an atm_pseudohdr on it and just send it directly. * [for native mode ATM output] if dst is null, then * rt0 must also be NULL. */ int atm_output(ifp, m0, dst, rt0) register struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; struct rtentry *rt0; { u_int16_t etype = 0; /* if using LLC/SNAP */ int s, error = 0, sz; struct atm_pseudohdr atmdst, *ad; struct mbuf *m = m0; struct rtentry *rt; struct atmllc *atmllc; struct atmllc *llc_hdr = NULL; u_int32_t atm_flags; if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); /* * check route */ if ((rt = rt0) != NULL) { if ((rt->rt_flags & RTF_UP) == 0) { /* route went down! */ if ((rt0 = rt = RTALLOC1(dst, 0)) != NULL) rt->rt_refcnt--; else senderr(EHOSTUNREACH); } if (rt->rt_flags & RTF_GATEWAY) { if (rt->rt_gwroute == 0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { rtfree(rt); rt = rt0; lookup: rt->rt_gwroute = RTALLOC1(rt->rt_gateway, 0); if ((rt = rt->rt_gwroute) == 0) senderr(EHOSTUNREACH); } } /* XXX: put RTF_REJECT code here if doing ATMARP */ } /* * check for non-native ATM traffic (dst != NULL) */ if (dst) { switch (dst->sa_family) { #if defined(INET) || defined(INET6) case AF_INET: case AF_INET6: if (dst->sa_family == AF_INET6) etype = htons(ETHERTYPE_IPV6); else etype = htons(ETHERTYPE_IP); if (!atmresolve(rt, m, dst, &atmdst)) { m = NULL; /* XXX: atmresolve already free'd it */ senderr(EHOSTUNREACH); /* XXX: put ATMARP stuff here */ /* XXX: watch who frees m on failure */ } break; #endif /* INET || INET6 */ case AF_UNSPEC: /* * XXX: bpfwrite. assuming dst contains 12 bytes * (atm pseudo header (4) + LLC/SNAP (8)) */ bcopy(dst->sa_data, &atmdst, sizeof(atmdst)); llc_hdr = (struct atmllc *)(dst->sa_data + sizeof(atmdst)); break; default: #if defined(__NetBSD__) || defined(__OpenBSD__) printf("%s: can't handle af%d\n", ifp->if_xname, dst->sa_family); #elif defined(__FreeBSD__) || defined(__bsdi__) printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, dst->sa_family); #endif senderr(EAFNOSUPPORT); } /* * must add atm_pseudohdr to data */ sz = sizeof(atmdst); atm_flags = ATM_PH_FLAGS(&atmdst); if (atm_flags & ATM_PH_LLCSNAP) sz += 8; /* sizeof snap == 8 */ M_PREPEND(m, sz, M_DONTWAIT); if (m == 0) senderr(ENOBUFS); ad = mtod(m, struct atm_pseudohdr *); *ad = atmdst; if (atm_flags & ATM_PH_LLCSNAP) { atmllc = (struct atmllc *)(ad + 1); if (llc_hdr == NULL) { bcopy(ATMLLC_HDR, atmllc->llchdr, sizeof(atmllc->llchdr)); ATM_LLC_SETTYPE(atmllc, etype); /* note: already in network order */ } else bcopy(llc_hdr, atmllc, sizeof(struct atmllc)); } } /* * Queue message on interface, and start output if interface * not yet active. */ if (! IF_HANDOFF(&ifp->if_snd, m, ifp)) return (ENOBUFS); return (error); bad: if (m) m_freem(m); return (error); } /* * Process a received ATM packet; * the packet is in the mbuf chain m. */ void atm_input(ifp, ah, m, rxhand) struct ifnet *ifp; register struct atm_pseudohdr *ah; struct mbuf *m; void *rxhand; { register struct ifqueue *inq; u_int16_t etype = ETHERTYPE_IP; /* default */ int s; if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } ifp->if_ibytes += m->m_pkthdr.len; if (rxhand) { #ifdef NATM struct natmpcb *npcb = rxhand; s = splimp(); /* in case 2 atm cards @ diff lvls */ npcb->npcb_inq++; /* count # in queue */ splx(s); schednetisr(NETISR_NATM); inq = &natmintrq; m->m_pkthdr.rcvif = rxhand; /* XXX: overload */ #else printf("atm_input: NATM detected but not configured in kernel\n"); m_freem(m); return; #endif } else { /* * handle LLC/SNAP header, if present */ if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) { struct atmllc *alc; if (m->m_len < sizeof(*alc) && (m = m_pullup(m, sizeof(*alc))) == 0) return; /* failed */ alc = mtod(m, struct atmllc *); if (bcmp(alc, ATMLLC_HDR, 6)) { #if defined(__NetBSD__) || defined(__OpenBSD__) printf("%s: recv'd invalid LLC/SNAP frame [vp=%d,vc=%d]\n", ifp->if_xname, ATM_PH_VPI(ah), ATM_PH_VCI(ah)); #elif defined(__FreeBSD__) || defined(__bsdi__) printf("%s%d: recv'd invalid LLC/SNAP frame [vp=%d,vc=%d]\n", ifp->if_name, ifp->if_unit, ATM_PH_VPI(ah), ATM_PH_VCI(ah)); #endif m_freem(m); return; } etype = ATM_LLC_TYPE(alc); m_adj(m, sizeof(*alc)); } switch (etype) { #ifdef INET case ETHERTYPE_IP: schednetisr(NETISR_IP); inq = &ipintrq; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: schednetisr(NETISR_IPV6); inq = &ip6intrq; break; #endif default: m_freem(m); return; } } (void) IF_HANDOFF(inq, m, NULL); } /* * Perform common duties while attaching to interface list */ void atm_ifattach(ifp) register struct ifnet *ifp; { register struct ifaddr *ifa; register struct sockaddr_dl *sdl; ifp->if_type = IFT_ATM; ifp->if_addrlen = 0; ifp->if_hdrlen = 0; ifp->if_mtu = ATMMTU; ifp->if_output = atm_output; ifp->if_snd.ifq_maxlen = 50; /* dummy */ #if defined(__NetBSD__) || defined(__OpenBSD__) - for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; - ifa = ifa->ifa_list.tqe_next) + for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != 0; + ifa = TAILQ_NEXT(ifa, ifa_list)) #elif defined(__FreeBSD__) && (__FreeBSD__ > 2) - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) #elif defined(__FreeBSD__) || defined(__bsdi__) for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) #endif if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && sdl->sdl_family == AF_LINK) { sdl->sdl_type = IFT_ATM; sdl->sdl_alen = ifp->if_addrlen; #ifdef notyet /* if using ATMARP, store hardware address using the next line */ bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen); #endif break; } } Index: head/sys/net/if_ef.c =================================================================== --- head/sys/net/if_ef.c (revision 71958) +++ head/sys/net/if_ef.c (revision 71959) @@ -1,615 +1,615 @@ /*- * Copyright (c) 1999, 2000 Boris Popov * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include "opt_inet.h" #include "opt_ipx.h" #include "opt_ef.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #endif #ifdef IPX #include #include #endif /* internal frame types */ #define ETHER_FT_EII 0 /* Ethernet_II - default */ #define ETHER_FT_8023 1 /* 802.3 (Novell) */ #define ETHER_FT_8022 2 /* 802.2 */ #define ETHER_FT_SNAP 3 /* SNAP */ #define EF_NFT 4 /* total number of frame types */ #ifdef EF_DEBUG #define EFDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args) #else #define EFDEBUG(format, args...) #endif #define EFERROR(format, args...) printf("%s: "format, __FUNCTION__ ,## args) struct efnet { struct arpcom ef_ac; struct ifnet * ef_ifp; }; struct ef_link { SLIST_ENTRY(ef_link) el_next; struct ifnet *el_ifp; /* raw device for this clones */ struct efnet *el_units[EF_NFT]; /* our clones */ }; static SLIST_HEAD(ef_link_head, ef_link) efdev = {NULL}; static int efcount; extern int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m); extern int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp, int *hlen); /* static void ef_reset (struct ifnet *); */ static int ef_attach(struct efnet *sc); static int ef_detach(struct efnet *sc); static void ef_init(void *); static int ef_ioctl(struct ifnet *, u_long, caddr_t); static void ef_start(struct ifnet *); static int ef_input(struct ifnet*, struct ether_header *, struct mbuf *); static int ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp, int *hlen); static int ef_load(void); static int ef_unload(void); /* * Install the interface, most of structure initialization done in ef_clone() */ static int ef_attach(struct efnet *sc) { struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if; struct ifaddr *ifa1, *ifa2; struct sockaddr_dl *sdl1, *sdl2; ifp->if_output = ether_output; ifp->if_start = ef_start; ifp->if_watchdog = NULL; ifp->if_init = ef_init; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); /* * Attach the interface */ ether_ifattach(ifp, ETHER_BPF_SUPPORTED); ifp->if_resolvemulti = 0; ifp->if_type = IFT_XETHER; ifp->if_flags |= IFF_RUNNING; ifa1 = ifnet_addrs[ifp->if_index - 1]; ifa2 = ifnet_addrs[sc->ef_ifp->if_index - 1]; sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; sdl1->sdl_type = IFT_ETHER; sdl1->sdl_alen = ETHER_ADDR_LEN; bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); bcopy(LLADDR(sdl2), sc->ef_ac.ac_enaddr, ETHER_ADDR_LEN); EFDEBUG("%s%d: attached\n", ifp->if_name, ifp->if_unit); return 1; } /* * This is for _testing_only_, just removes interface from interfaces list */ static int ef_detach(struct efnet *sc) { struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if; int s; s = splimp(); if (ifp->if_flags & IFF_UP) { if_down(ifp); if (ifp->if_flags & IFF_RUNNING) { /* find internet addresses and delete routes */ register struct ifaddr *ifa; - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) { rtinit(ifa, (int)RTM_DELETE, 0); } } } TAILQ_REMOVE(&ifnet, ifp, if_link); splx(s); return 0; } static void ef_init(void *foo) { return; } static int ef_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { /* struct ef_link *sc = (struct ef_link*)ifp->if_softc;*/ struct ifaddr *ifa = (struct ifaddr*)data; int s, error; EFDEBUG("IOCTL %ld for %s%d\n", cmd, ifp->if_name, ifp->if_unit); error = 0; s = splimp(); switch (cmd) { case SIOCSIFADDR: if (ifp->if_unit == ETHER_FT_8023 && ifa->ifa_addr->sa_family != AF_IPX) { error = EAFNOSUPPORT; break; } ifp->if_flags |= IFF_UP; /* FALL THROUGH */ case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, cmd, data); break; case SIOCSIFFLAGS: error = 0; break; default: error = EINVAL; } splx(s); return error; } /* * Currently packet prepared in the ether_output(), but this can be a better * place. */ static void ef_start(struct ifnet *ifp) { struct efnet *sc = (struct efnet*)ifp->if_softc; struct ifnet *p; struct mbuf *m; ifp->if_flags |= IFF_OACTIVE; p = sc->ef_ifp; EFDEBUG("\n"); for (;;) { IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) break; if (ifp->if_bpf) bpf_mtap(ifp, m); if (! IF_HANDOFF(&p->if_snd, m, p)) { ifp->if_oerrors++; continue; } ifp->if_opackets++; } ifp->if_flags &= ~IFF_OACTIVE; return; } /* * Inline functions do not put additional overhead to procedure call or * parameter passing but simplify the code */ static int __inline ef_inputEII(struct mbuf *m, struct ether_header *eh, struct llc* l, u_short ether_type, struct ifqueue **inq) { switch(ether_type) { #ifdef IPX case ETHERTYPE_IPX: schednetisr(NETISR_IPX); *inq = &ipxintrq; break; #endif #ifdef INET case ETHERTYPE_IP: if (ipflow_fastforward(m)) return 1; schednetisr(NETISR_IP); *inq = &ipintrq; break; case ETHERTYPE_ARP: schednetisr(NETISR_ARP); *inq = &arpintrq; break; #endif default: return EPROTONOSUPPORT; } return 0; } static int __inline ef_inputSNAP(struct mbuf *m, struct ether_header *eh, struct llc* l, u_short ether_type, struct ifqueue **inq) { switch(ether_type) { #ifdef IPX case ETHERTYPE_IPX: m_adj(m, 8); schednetisr(NETISR_IPX); *inq = &ipxintrq; break; #endif default: return EPROTONOSUPPORT; } return 0; } static int __inline ef_input8022(struct mbuf *m, struct ether_header *eh, struct llc* l, u_short ether_type, struct ifqueue **inq) { switch(ether_type) { #ifdef IPX case 0xe0: m_adj(m, 3); schednetisr(NETISR_IPX); *inq = &ipxintrq; break; #endif default: return EPROTONOSUPPORT; } return 0; } /* * Called from ether_input() */ static int ef_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m) { u_short ether_type; int ft = -1; struct ifqueue *inq; struct efnet *efp; struct ifnet *eifp; struct llc *l; struct ef_link *efl; ether_type = ntohs(eh->ether_type); if (ether_type < ETHERMTU) { l = mtod(m, struct llc*); if (l->llc_dsap == 0xff && l->llc_ssap == 0xff) { /* * Novell's "802.3" frame */ ft = ETHER_FT_8023; } else if (l->llc_dsap == 0xaa && l->llc_ssap == 0xaa) { /* * 802.2/SNAP */ ft = ETHER_FT_SNAP; ether_type = ntohs(l->llc_un.type_snap.ether_type); } else if (l->llc_dsap == l->llc_ssap) { /* * 802.3/802.2 */ ft = ETHER_FT_8022; ether_type = l->llc_ssap; } } else ft = ETHER_FT_EII; if (ft == -1) { EFDEBUG("Unrecognised ether_type %x\n", ether_type); return EPROTONOSUPPORT; } /* * Check if interface configured for the given frame */ efp = NULL; SLIST_FOREACH(efl, &efdev, el_next) { if (efl->el_ifp == ifp) { efp = efl->el_units[ft]; break; } } if (efp == NULL) { EFDEBUG("Can't find if for %d\n", ft); return EPROTONOSUPPORT; } eifp = &efp->ef_ac.ac_if; if ((eifp->if_flags & IFF_UP) == 0) return EPROTONOSUPPORT; eifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh); m->m_pkthdr.rcvif = eifp; if (eifp->if_bpf) { struct mbuf m0; m0.m_next = m; m0.m_len = sizeof(struct ether_header); m0.m_data = (char *)eh; bpf_mtap(eifp, &m0); } /* * Now we ready to adjust mbufs and pass them to protocol intr's */ inq = NULL; switch(ft) { case ETHER_FT_EII: if (ef_inputEII(m, eh, l, ether_type, &inq) != 0) return EPROTONOSUPPORT; break; #ifdef IPX case ETHER_FT_8023: /* only IPX can be here */ schednetisr(NETISR_IPX); inq = &ipxintrq; break; #endif case ETHER_FT_SNAP: if (ef_inputSNAP(m, eh, l, ether_type, &inq) != 0) return EPROTONOSUPPORT; break; case ETHER_FT_8022: if (ef_input8022(m, eh, l, ether_type, &inq) != 0) return EPROTONOSUPPORT; break; } if (inq == NULL) { EFDEBUG("No support for frame %d and proto %04x\n", ft, ether_type); return EPROTONOSUPPORT; } (void) IF_HANDOFF(inq, m, NULL); return 0; } static int ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp, int *hlen) { struct mbuf *m = *mp; u_char *cp; short type; if (ifp->if_type != IFT_XETHER) return ENETDOWN; switch (ifp->if_unit) { case ETHER_FT_EII: #ifdef IPX type = htons(ETHERTYPE_IPX); #else return EPFNOSUPPORT; #endif break; case ETHER_FT_8023: type = htons(m->m_pkthdr.len); break; case ETHER_FT_8022: M_PREPEND(m, ETHER_HDR_LEN + 3, M_TRYWAIT); if (m == NULL) { *mp = NULL; return ENOBUFS; } /* * Ensure that ethernet header and next three bytes * will fit into single mbuf */ m = m_pullup(m, ETHER_HDR_LEN + 3); if (m == NULL) { *mp = NULL; return ENOBUFS; } m_adj(m, ETHER_HDR_LEN); type = htons(m->m_pkthdr.len); cp = mtod(m, u_char *); *cp++ = 0xE0; *cp++ = 0xE0; *cp++ = 0x03; *hlen += 3; break; case ETHER_FT_SNAP: M_PREPEND(m, 8, M_TRYWAIT); if (m == NULL) { *mp = NULL; return ENOBUFS; } type = htons(m->m_pkthdr.len); cp = mtod(m, u_char *); bcopy("\xAA\xAA\x03\x00\x00\x00\x81\x37", cp, 8); *hlen += 8; break; default: return EPFNOSUPPORT; } *mp = m; *tp = type; return 0; } /* * Create clone from the given interface */ static int ef_clone(struct ef_link *efl, int ft) { struct efnet *efp; struct ifnet *eifp; struct ifnet *ifp = efl->el_ifp; char cbuf[IFNAMSIZ], *ifname; int ifnlen; efp = (struct efnet*)malloc(sizeof(struct efnet), M_IFADDR, M_WAITOK | M_ZERO); if (efp == NULL) return ENOMEM; efp->ef_ifp = ifp; eifp = &efp->ef_ac.ac_if; ifnlen = 1 + snprintf(cbuf, sizeof(cbuf), "%s%df", ifp->if_name, ifp->if_unit); ifname = (char*)malloc(ifnlen, M_IFADDR, M_WAITOK); eifp->if_name = strcpy(ifname, cbuf); eifp->if_unit = ft; eifp->if_softc = efp; if (ifp->if_ioctl) eifp->if_ioctl = ef_ioctl; efl->el_units[ft] = efp; return 0; } static int ef_load(void) { struct ifnet *ifp; struct efnet *efp; struct ef_link *efl = NULL; int error = 0, d; - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) { if (ifp->if_type != IFT_ETHER) continue; EFDEBUG("Found interface %s%d\n", ifp->if_name, ifp->if_unit); efl = (struct ef_link*)malloc(sizeof(struct ef_link), M_IFADDR, M_WAITOK); if (efl == NULL) { error = ENOMEM; break; } bzero(efl, sizeof(*efl)); efl->el_ifp = ifp; #ifdef ETHER_II error = ef_clone(efl, ETHER_FT_EII); if (error) break; #endif #ifdef ETHER_8023 error = ef_clone(efl, ETHER_FT_8023); if (error) break; #endif #ifdef ETHER_8022 error = ef_clone(efl, ETHER_FT_8022); if (error) break; #endif #ifdef ETHER_SNAP error = ef_clone(efl, ETHER_FT_SNAP); if (error) break; #endif efcount++; SLIST_INSERT_HEAD(&efdev, efl, el_next); } if (error) { if (efl) SLIST_INSERT_HEAD(&efdev, efl, el_next); SLIST_FOREACH(efl, &efdev, el_next) { for (d = 0; d < EF_NFT; d++) if (efl->el_units[d]) free(efl->el_units[d], M_IFADDR); free(efl, M_IFADDR); } return error; } SLIST_FOREACH(efl, &efdev, el_next) { for (d = 0; d < EF_NFT; d++) { efp = efl->el_units[d]; if (efp) ef_attach(efp); } } ef_inputp = ef_input; ef_outputp = ef_output; EFDEBUG("Loaded\n"); return 0; } static int ef_unload(void) { struct efnet *efp; struct ef_link *efl; int d; ef_inputp = NULL; ef_outputp = NULL; SLIST_FOREACH(efl, &efdev, el_next) { for (d = 0; d < EF_NFT; d++) { efp = efl->el_units[d]; if (efp) { ef_detach(efp); } } } EFDEBUG("Unloaded\n"); return 0; } static int if_ef_modevent(module_t mod, int type, void *data) { switch ((modeventtype_t)type) { case MOD_LOAD: return ef_load(); case MOD_UNLOAD: return ef_unload(); default: break; } return 0; } static moduledata_t if_ef_mod = { "if_ef", if_ef_modevent, NULL }; DECLARE_MODULE(if_ef, if_ef_mod, SI_SUB_PSEUDO, SI_ORDER_MIDDLE); Index: head/sys/net/if_fddisubr.c =================================================================== --- head/sys/net/if_fddisubr.c (revision 71958) +++ head/sys/net/if_fddisubr.c (revision 71959) @@ -1,637 +1,637 @@ /* * Copyright (c) 1995, 1996 * Matt Thomas . All rights reserved. * Copyright (c) 1982, 1989, 1993 * The 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: if_ethersubr.c,v 1.5 1994/12/13 22:31:45 wollman Exp * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #endif #ifdef INET6 #include #endif #if defined(__FreeBSD__) #include #else #include #endif #ifdef IPX #include #include #endif #ifdef NS #include #include #endif #ifdef DECNET #include #endif #ifdef NETATALK #include #include #include #define llc_snap_org_code llc_un.type_snap.org_code #define llc_snap_ether_type llc_un.type_snap.ether_type extern u_char at_org_code[ 3 ]; extern u_char aarp_org_code[ 3 ]; #endif /* NETATALK */ static int fddi_resolvemulti __P((struct ifnet *, struct sockaddr **, struct sockaddr *)); #define senderr(e) { error = (e); goto bad;} /* * This really should be defined in if_llc.h but in case it isn't. */ #ifndef llc_snap #define llc_snap llc_un.type_snap #endif #if defined(__bsdi__) || defined(__NetBSD__) #define RTALLOC1(a, b) rtalloc1(a, b) #define ARPRESOLVE(a, b, c, d, e, f) arpresolve(a, b, c, d, e) #elif defined(__FreeBSD__) #define RTALLOC1(a, b) rtalloc1(a, b, 0UL) #define ARPRESOLVE(a, b, c, d, e, f) arpresolve(a, b, c, d, e, f) #endif /* * FDDI output routine. * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first * packet leaves a multiple of 512 bytes of data in remainder. * Assumes that ifp is actually pointer to arpcom structure. */ int fddi_output(ifp, m, dst, rt0) register struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt0; { u_int16_t type; int loop_copy = 0, error = 0, hdrcmplt = 0; u_char esrc[6], edst[6]; register struct rtentry *rt; register struct fddi_header *fh; struct arpcom *ac = (struct arpcom *)ifp; if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); getmicrotime(&ifp->if_lastchange); #if !defined(__bsdi__) || _BSDI_VERSION >= 199401 if ((rt = rt0) != NULL) { if ((rt->rt_flags & RTF_UP) == 0) { if ((rt0 = rt = RTALLOC1(dst, 1)) != NULL) rt->rt_refcnt--; else senderr(EHOSTUNREACH); } if (rt->rt_flags & RTF_GATEWAY) { if (rt->rt_gwroute == 0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { rtfree(rt); rt = rt0; lookup: rt->rt_gwroute = RTALLOC1(rt->rt_gateway, 1); if ((rt = rt->rt_gwroute) == 0) senderr(EHOSTUNREACH); } } if (rt->rt_flags & RTF_REJECT) if (rt->rt_rmx.rmx_expire == 0 || time_second < rt->rt_rmx.rmx_expire) senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } #endif switch (dst->sa_family) { #ifdef INET case AF_INET: { #if !defined(__bsdi__) || _BSDI_VERSION >= 199401 if (!ARPRESOLVE(ac, rt, m, dst, edst, rt0)) return (0); /* if not yet resolved */ #else int usetrailers; if (!arpresolve(ac, m, &((struct sockaddr_in *)dst)->sin_addr, edst, &usetrailers)) return (0); /* if not yet resolved */ #endif type = htons(ETHERTYPE_IP); break; } #endif #ifdef INET6 case AF_INET6: if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) { /* this must be impossible, so we bark */ printf("nd6_storelladdr failed\n"); return(0); } type = htons(ETHERTYPE_IPV6); break; #endif #ifdef IPX case AF_IPX: type = htons(ETHERTYPE_IPX); bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host), (caddr_t)edst, sizeof (edst)); break; #endif #ifdef NETATALK case AF_APPLETALK: { struct at_ifaddr *aa; if (!aarpresolve(ac, m, (struct sockaddr_at *)dst, edst)) return (0); /* * ifaddr is the first thing in at_ifaddr */ if ((aa = at_ifawithnet( (struct sockaddr_at *)dst)) == 0) goto bad; /* * In the phase 2 case, we need to prepend an mbuf for the llc header. * Since we must preserve the value of m, which is passed to us by * value, we m_copy() the first mbuf, and use it for our llc header. */ if (aa->aa_flags & AFA_PHASE2) { struct llc llc; M_PREPEND(m, sizeof(struct llc), M_TRYWAIT); if (m == 0) senderr(ENOBUFS); llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP; llc.llc_control = LLC_UI; bcopy(at_org_code, llc.llc_snap_org_code, sizeof(at_org_code)); llc.llc_snap_ether_type = htons(ETHERTYPE_AT); bcopy(&llc, mtod(m, caddr_t), sizeof(struct llc)); type = 0; } else { type = htons(ETHERTYPE_AT); } break; } #endif /* NETATALK */ #ifdef NS case AF_NS: type = htons(ETHERTYPE_NS); bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), (caddr_t)edst, sizeof (edst)); break; #endif case pseudo_AF_HDRCMPLT: { struct ether_header *eh; hdrcmplt = 1; eh = (struct ether_header *)dst->sa_data; (void)memcpy((caddr_t)esrc, (caddr_t)eh->ether_shost, sizeof (esrc)); /* FALLTHROUGH */ } case AF_UNSPEC: { struct ether_header *eh; loop_copy = -1; eh = (struct ether_header *)dst->sa_data; (void)memcpy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); if (*edst & 1) m->m_flags |= (M_BCAST|M_MCAST); type = eh->ether_type; break; } case AF_IMPLINK: { fh = mtod(m, struct fddi_header *); error = EPROTONOSUPPORT; switch (fh->fddi_fc & (FDDIFC_C|FDDIFC_L|FDDIFC_F)) { case FDDIFC_LLC_ASYNC: { /* legal priorities are 0 through 7 */ if ((fh->fddi_fc & FDDIFC_Z) > 7) goto bad; break; } case FDDIFC_LLC_SYNC: { /* FDDIFC_Z bits reserved, must be zero */ if (fh->fddi_fc & FDDIFC_Z) goto bad; break; } case FDDIFC_SMT: { /* FDDIFC_Z bits must be non zero */ if ((fh->fddi_fc & FDDIFC_Z) == 0) goto bad; break; } default: { /* anything else is too dangerous */ goto bad; } } error = 0; if (fh->fddi_dhost[0] & 1) m->m_flags |= (M_BCAST|M_MCAST); goto queue_it; } default: printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, dst->sa_family); senderr(EAFNOSUPPORT); } if (type != 0) { register struct llc *l; M_PREPEND(m, sizeof (struct llc), M_DONTWAIT); if (m == 0) senderr(ENOBUFS); l = mtod(m, struct llc *); l->llc_control = LLC_UI; l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP; l->llc_snap.org_code[0] = l->llc_snap.org_code[1] = l->llc_snap.org_code[2] = 0; (void)memcpy((caddr_t) &l->llc_snap.ether_type, (caddr_t) &type, sizeof(u_int16_t)); } /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, sizeof (struct fddi_header), M_DONTWAIT); if (m == 0) senderr(ENOBUFS); fh = mtod(m, struct fddi_header *); fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4; (void)memcpy((caddr_t)fh->fddi_dhost, (caddr_t)edst, sizeof (edst)); queue_it: if (hdrcmplt) (void)memcpy((caddr_t)fh->fddi_shost, (caddr_t)esrc, sizeof(fh->fddi_shost)); else (void)memcpy((caddr_t)fh->fddi_shost, (caddr_t)ac->ac_enaddr, sizeof(fh->fddi_shost)); /* * If a simplex interface, and the packet is being sent to our * Ethernet address or a broadcast address, loopback a copy. * XXX To make a simplex device behave exactly like a duplex * device, we should copy in the case of sending to our own * ethernet address (thus letting the original actually appear * on the wire). However, we don't do that here for security * reasons and compatibility with the original behavior. */ if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) { if ((m->m_flags & M_BCAST) || loop_copy) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); (void) if_simloop(ifp, n, dst->sa_family, sizeof(struct fddi_header)); } else if (bcmp(fh->fddi_dhost, fh->fddi_shost, sizeof(fh->fddi_shost)) == 0) { (void) if_simloop(ifp, m, dst->sa_family, sizeof(struct fddi_header)); return(0); /* XXX */ } } if (! IF_HANDOFF(&ifp->if_snd, m, ifp)) senderr(ENOBUFS); return (error); bad: if (m) m_freem(m); return (error); } /* * Process a received FDDI packet; * the packet is in the mbuf chain m without * the fddi header, which is provided separately. */ void fddi_input(ifp, fh, m) struct ifnet *ifp; register struct fddi_header *fh; struct mbuf *m; { register struct ifqueue *inq; register struct llc *l; if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } getmicrotime(&ifp->if_lastchange); ifp->if_ibytes += m->m_pkthdr.len + sizeof (*fh); if (fh->fddi_dhost[0] & 1) { if (bcmp((caddr_t)fddibroadcastaddr, (caddr_t)fh->fddi_dhost, sizeof(fddibroadcastaddr)) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; ifp->if_imcasts++; } else if ((ifp->if_flags & IFF_PROMISC) && bcmp(((struct arpcom *)ifp)->ac_enaddr, (caddr_t)fh->fddi_dhost, sizeof(fh->fddi_dhost)) != 0) { m_freem(m); return; } #ifdef M_LINK0 /* * If this has a LLC priority of 0, then mark it so upper * layers have a hint that it really came via a FDDI/Ethernet * bridge. */ if ((fh->fddi_fc & FDDIFC_LLC_PRIO7) == FDDIFC_LLC_PRIO0) m->m_flags |= M_LINK0; #endif l = mtod(m, struct llc *); switch (l->llc_dsap) { #if defined(INET) || defined(INET6) || defined(NS) || defined(DECNET) || defined(IPX) || defined(NETATALK) case LLC_SNAP_LSAP: { u_int16_t type; if (l->llc_control != LLC_UI || l->llc_ssap != LLC_SNAP_LSAP) goto dropanyway; #ifdef NETATALK if (Bcmp(&(l->llc_snap_org_code)[0], at_org_code, sizeof(at_org_code)) == 0 && ntohs(l->llc_snap_ether_type) == ETHERTYPE_AT) { inq = &atintrq2; m_adj( m, sizeof( struct llc )); schednetisr(NETISR_ATALK); break; } if (Bcmp(&(l->llc_snap_org_code)[0], aarp_org_code, sizeof(aarp_org_code)) == 0 && ntohs(l->llc_snap_ether_type) == ETHERTYPE_AARP) { m_adj( m, sizeof( struct llc )); aarpinput((struct arpcom *)ifp, m); /* XXX */ return; } #endif /* NETATALK */ if (l->llc_snap.org_code[0] != 0 || l->llc_snap.org_code[1] != 0|| l->llc_snap.org_code[2] != 0) goto dropanyway; type = ntohs(l->llc_snap.ether_type); m_adj(m, 8); switch (type) { #ifdef INET case ETHERTYPE_IP: if (ipflow_fastforward(m)) return; schednetisr(NETISR_IP); inq = &ipintrq; break; case ETHERTYPE_ARP: #if !defined(__bsdi__) || _BSDI_VERSION >= 199401 schednetisr(NETISR_ARP); inq = &arpintrq; break; #else arpinput((struct arpcom *)ifp, m); return; #endif #endif #ifdef INET6 case ETHERTYPE_IPV6: schednetisr(NETISR_IPV6); inq = &ip6intrq; break; #endif #ifdef IPX case ETHERTYPE_IPX: schednetisr(NETISR_IPX); inq = &ipxintrq; break; #endif #ifdef NS case ETHERTYPE_NS: schednetisr(NETISR_NS); inq = &nsintrq; break; #endif #ifdef DECNET case ETHERTYPE_DECNET: schednetisr(NETISR_DECNET); inq = &decnetintrq; break; #endif #ifdef NETATALK case ETHERTYPE_AT: schednetisr(NETISR_ATALK); inq = &atintrq1; break; case ETHERTYPE_AARP: /* probably this should be done with a NETISR as well */ aarpinput((struct arpcom *)ifp, m); /* XXX */ return; #endif /* NETATALK */ default: /* printf("fddi_input: unknown protocol 0x%x\n", type); */ ifp->if_noproto++; goto dropanyway; } break; } #endif /* INET || NS */ default: /* printf("fddi_input: unknown dsap 0x%x\n", l->llc_dsap); */ ifp->if_noproto++; dropanyway: m_freem(m); return; } (void) IF_HANDOFF(inq, m, NULL); } /* * Perform common duties while attaching to interface list */ #ifdef __NetBSD__ #define ifa_next ifa_list.tqe_next #endif void fddi_ifattach(ifp) register struct ifnet *ifp; { register struct ifaddr *ifa; register struct sockaddr_dl *sdl; ifp->if_type = IFT_FDDI; ifp->if_addrlen = 6; ifp->if_hdrlen = 21; ifp->if_mtu = FDDIMTU; ifp->if_resolvemulti = fddi_resolvemulti; ifp->if_baudrate = 100000000; #ifdef IFF_NOTRAILERS ifp->if_flags |= IFF_NOTRAILERS; #endif #if defined(__FreeBSD__) ifa = ifnet_addrs[ifp->if_index - 1]; sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_FDDI; sdl->sdl_alen = ifp->if_addrlen; bcopy(((struct arpcom *)ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); #elif defined(__NetBSD__) LIST_INIT(&((struct arpcom *)ifp)->ac_multiaddrs); - for (ifa = ifp->if_addrlist.tqh_first; ifa != NULL; ifa = ifa->ifa_list.tqe_next) + for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = TAILQ_NEXT(ifa, ifa_list)) #else for (ifa = ifp->if_addrlist; ifa != NULL; ifa = ifa->ifa_next) #endif #if !defined(__FreeBSD__) if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && sdl->sdl_family == AF_LINK) { sdl->sdl_type = IFT_FDDI; sdl->sdl_alen = ifp->if_addrlen; bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); break; } #endif } static int fddi_resolvemulti(ifp, llsa, sa) struct ifnet *ifp; struct sockaddr **llsa; struct sockaddr *sa; { struct sockaddr_dl *sdl; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif u_char *e_addr; switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; e_addr = LLADDR(sdl); if ((e_addr[0] & 1) != 1) return EADDRNOTAVAIL; *llsa = 0; return 0; #ifdef INET case AF_INET: sin = (struct sockaddr_in *)sa; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) return EADDRNOTAVAIL; MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, M_WAITOK); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_FDDI; sdl->sdl_nlen = 0; sdl->sdl_alen = ETHER_ADDR_LEN; /* XXX */ sdl->sdl_slen = 0; e_addr = LLADDR(sdl); ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* * An IP6 address of 0 means listen to all * of the Ethernet multicast address used for IP6. * (This is used for multicast routers.) */ ifp->if_flags |= IFF_ALLMULTI; *llsa = 0; return 0; } if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return EADDRNOTAVAIL; MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, M_WAITOK); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_FDDI; sdl->sdl_nlen = 0; sdl->sdl_alen = ETHER_ADDR_LEN; /* XXX */ sdl->sdl_slen = 0; e_addr = LLADDR(sdl); ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif default: /* * Well, the text isn't quite right, but it's the name * that counts... */ return EAFNOSUPPORT; } } Index: head/sys/net/if_media.c =================================================================== --- head/sys/net/if_media.c (revision 71958) +++ head/sys/net/if_media.c (revision 71959) @@ -1,487 +1,487 @@ /* $NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1997 * Jonathan Stone and Jason R. Thorpe. All rights reserved. * * This software is derived from information provided by Matt Thomas. * * 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 Jonathan Stone * and Jason R. Thorpe for the NetBSD Project. * 4. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHOR 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. */ /* * BSD/OS-compatible network interface media selection. * * Where it is safe to do so, this code strays slightly from the BSD/OS * design. Software which uses the API (device drivers, basically) * shouldn't notice any difference. * * Many thanks to Matt Thomas for providing the information necessary * to implement this interface. */ #include #include #include #include #include #include #include /* * Compile-time options: * IFMEDIA_DEBUG: * turn on implementation-level debug printfs. * Useful for debugging newly-ported drivers. */ static struct ifmedia_entry *ifmedia_match __P((struct ifmedia *ifm, int flags, int mask)); #ifdef IFMEDIA_DEBUG int ifmedia_debug = 0; static void ifmedia_printword __P((int)); #endif /* * Initialize if_media struct for a specific interface instance. */ void ifmedia_init(ifm, dontcare_mask, change_callback, status_callback) struct ifmedia *ifm; int dontcare_mask; ifm_change_cb_t change_callback; ifm_stat_cb_t status_callback; { LIST_INIT(&ifm->ifm_list); ifm->ifm_cur = NULL; ifm->ifm_media = 0; ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ ifm->ifm_change = change_callback; ifm->ifm_status = status_callback; } void ifmedia_removeall(ifm) struct ifmedia *ifm; { struct ifmedia_entry *entry; for (entry = LIST_FIRST(&ifm->ifm_list); entry; entry = LIST_FIRST(&ifm->ifm_list)) { LIST_REMOVE(entry, ifm_list); free(entry, M_IFADDR); } } /* * Add a media configuration to the list of supported media * for a specific interface instance. */ void ifmedia_add(ifm, mword, data, aux) struct ifmedia *ifm; int mword; int data; void *aux; { register struct ifmedia_entry *entry; #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { if (ifm == NULL) { printf("ifmedia_add: null ifm\n"); return; } printf("Adding entry for "); ifmedia_printword(mword); } #endif entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT); if (entry == NULL) panic("ifmedia_add: can't malloc entry"); entry->ifm_media = mword; entry->ifm_data = data; entry->ifm_aux = aux; LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list); } /* * Add an array of media configurations to the list of * supported media for a specific interface instance. */ void ifmedia_list_add(ifm, lp, count) struct ifmedia *ifm; struct ifmedia_entry *lp; int count; { int i; for (i = 0; i < count; i++) ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, lp[i].ifm_aux); } /* * Set the default active media. * * Called by device-specific code which is assumed to have already * selected the default media in hardware. We do _not_ call the * media-change callback. */ void ifmedia_set(ifm, target) struct ifmedia *ifm; int target; { struct ifmedia_entry *match; match = ifmedia_match(ifm, target, ifm->ifm_mask); if (match == NULL) { printf("ifmedia_set: no match for 0x%x/0x%x\n", target, ~ifm->ifm_mask); panic("ifmedia_set"); } ifm->ifm_cur = match; #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { printf("ifmedia_set: target "); ifmedia_printword(target); printf("ifmedia_set: setting to "); ifmedia_printword(ifm->ifm_cur->ifm_media); } #endif } /* * Device-independent media ioctl support function. */ int ifmedia_ioctl(ifp, ifr, ifm, cmd) struct ifnet *ifp; struct ifreq *ifr; struct ifmedia *ifm; u_long cmd; { struct ifmedia_entry *match; struct ifmediareq *ifmr = (struct ifmediareq *) ifr; int error = 0, sticky; if (ifp == NULL || ifr == NULL || ifm == NULL) return(EINVAL); switch (cmd) { /* * Set the current media. */ case SIOCSIFMEDIA: { struct ifmedia_entry *oldentry; int oldmedia; int newmedia = ifr->ifr_media; match = ifmedia_match(ifm, newmedia, ifm->ifm_mask); if (match == NULL) { #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { printf( "ifmedia_ioctl: no media found for 0x%x\n", newmedia); } #endif return (ENXIO); } /* * If no change, we're done. * XXX Automedia may invole software intervention. * Keep going in case the the connected media changed. * Similarly, if best match changed (kernel debugger?). */ if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur)) return 0; /* * We found a match, now make the driver switch to it. * Make sure to preserve our old media type in case the * driver can't switch. */ #ifdef IFMEDIA_DEBUG if (ifmedia_debug) { printf("ifmedia_ioctl: switching %s%d to ", ifp->if_name, ifp->if_unit); ifmedia_printword(match->ifm_media); } #endif oldentry = ifm->ifm_cur; oldmedia = ifm->ifm_media; ifm->ifm_cur = match; ifm->ifm_media = newmedia; error = (*ifm->ifm_change)(ifp); if (error) { ifm->ifm_cur = oldentry; ifm->ifm_media = oldmedia; } break; } /* * Get list of available media and current media on interface. */ case SIOCGIFMEDIA: { struct ifmedia_entry *ep; int *kptr, count; kptr = NULL; /* XXX gcc */ ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? ifm->ifm_cur->ifm_media : IFM_NONE; ifmr->ifm_mask = ifm->ifm_mask; ifmr->ifm_status = 0; (*ifm->ifm_status)(ifp, ifmr); count = 0; - ep = ifm->ifm_list.lh_first; + ep = LIST_FIRST(&ifm->ifm_list); if (ifmr->ifm_count != 0) { kptr = (int *)malloc(ifmr->ifm_count * sizeof(int), M_TEMP, M_WAITOK); /* * Get the media words from the interface's list. */ for (; ep != NULL && count < ifmr->ifm_count; - ep = ep->ifm_list.le_next, count++) + ep = LIST_NEXT(ep, ifm_list), count++) kptr[count] = ep->ifm_media; if (ep != NULL) error = E2BIG; /* oops! */ } /* * If there are more interfaces on the list, count * them. This allows the caller to set ifmr->ifm_count * to 0 on the first call to know how much space to * callocate. */ - for (; ep != NULL; ep = ep->ifm_list.le_next) + for (; ep != NULL; ep = LIST_NEXT(ep, ifm_list)) count++; /* * We do the copyout on E2BIG, because that's * just our way of telling userland that there * are more. This is the behavior I've observed * under BSD/OS 3.0 */ sticky = error; if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) { error = copyout((caddr_t)kptr, (caddr_t)ifmr->ifm_ulist, ifmr->ifm_count * sizeof(int)); } if (error == 0) error = sticky; if (ifmr->ifm_count != 0) free(kptr, M_TEMP); ifmr->ifm_count = count; break; } default: return (EINVAL); } return (error); } /* * Find media entry matching a given ifm word. * */ static struct ifmedia_entry * ifmedia_match(ifm, target, mask) struct ifmedia *ifm; int target; int mask; { struct ifmedia_entry *match, *next; match = NULL; mask = ~mask; - for (next = ifm->ifm_list.lh_first; next != NULL; - next = next->ifm_list.le_next) { + for (next = LIST_FIRST(&ifm->ifm_list); next != NULL; + next = LIST_NEXT(next, ifm_list)) { if ((next->ifm_media & mask) == (target & mask)) { #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) if (match) { printf("ifmedia_match: multiple match for " "0x%x/0x%x\n", target, mask); } #endif match = next; } } return match; } #ifdef IFMEDIA_DEBUG struct ifmedia_description ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS; struct ifmedia_description ifm_subtype_ethernet_descriptions[] = IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; struct ifmedia_description ifm_subtype_tokenring_descriptions[] = IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; struct ifmedia_description ifm_subtype_fddi_descriptions[] = IFM_SUBTYPE_FDDI_DESCRIPTIONS; struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; struct ifmedia_description ifm_subtype_shared_descriptions[] = IFM_SUBTYPE_SHARED_DESCRIPTIONS; struct ifmedia_description ifm_shared_option_descriptions[] = IFM_SHARED_OPTION_DESCRIPTIONS; struct ifmedia_type_to_subtype { struct ifmedia_description *subtypes; struct ifmedia_description *options; }; /* must be in the same order as IFM_TYPE_DESCRIPTIONS */ struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { { &ifm_subtype_ethernet_descriptions[0], &ifm_subtype_ethernet_option_descriptions[0] }, { &ifm_subtype_tokenring_descriptions[0], &ifm_subtype_tokenring_option_descriptions[0] }, { &ifm_subtype_fddi_descriptions[0], &ifm_subtype_fddi_option_descriptions[0] }, }; /* * print a media word. */ static void ifmedia_printword(ifmw) int ifmw; { struct ifmedia_description *desc; struct ifmedia_type_to_subtype *ttos; int seen_option = 0; /* Find the top-level interface type. */ for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; desc->ifmt_string != NULL; desc++, ttos++) if (IFM_TYPE(ifmw) == desc->ifmt_word) break; if (desc->ifmt_string == NULL) { printf("\n"); return; } printf(desc->ifmt_string); /* * Check for the shared subtype descriptions first, then the * type-specific ones. */ for (desc = ifm_subtype_shared_descriptions; desc->ifmt_string != NULL; desc++) if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) goto got_subtype; for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++) if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) break; if (desc->ifmt_string == NULL) { printf(" \n"); return; } got_subtype: printf(" %s", desc->ifmt_string); /* * Look for shared options. */ for (desc = ifm_shared_option_descriptions; desc->ifmt_string != NULL; desc++) { if (ifmw & desc->ifmt_word) { if (seen_option == 0) printf(" <"); printf("%s%s", seen_option++ ? "," : "", desc->ifmt_string); } } /* * Look for subtype-specific options. */ for (desc = ttos->options; desc->ifmt_string != NULL; desc++) { if (ifmw & desc->ifmt_word) { if (seen_option == 0) printf(" <"); printf("%s%s", seen_option++ ? "," : "", desc->ifmt_string); } } printf("%s\n", seen_option ? ">" : ""); } #endif /* IFMEDIA_DEBUG */ Index: head/sys/net/if_spppsubr.c =================================================================== --- head/sys/net/if_spppsubr.c (revision 71958) +++ head/sys/net/if_spppsubr.c (revision 71959) @@ -1,4250 +1,4250 @@ /* * Synchronous PPP/Cisco link level subroutines. * Keepalive protocol implemented in both Cisco and PPP modes. * * Copyright (C) 1994-1996 Cronyx Engineering Ltd. * Author: Serge Vakulenko, * * Heavily revamped to conform to RFC 1661. * Copyright (C) 1997, Joerg Wunsch. * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * From: Version 2.4, Thu Apr 30 17:17:21 MSD 1997 * * $FreeBSD$ */ #include #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #endif #ifdef NetBSD1_3 # if NetBSD1_3 > 6 # include "opt_inet.h" # include "opt_inet6.h" # include "opt_iso.h" # endif #endif #include #include #include #include #include #include #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #include #endif #include #include #if defined (__OpenBSD__) #include #else #include #endif #include #include #include #include #if defined (__NetBSD__) || defined (__OpenBSD__) #include /* XXX for softnet */ #endif #include #ifdef INET #include #include #include #include #include # if defined (__FreeBSD__) || defined (__OpenBSD__) # include # else # include # endif #else # error Huh? sppp without INET? #endif #ifdef IPX #include #include #endif #ifdef NS #include #include #endif #include #if defined(__FreeBSD__) && __FreeBSD__ >= 3 # define UNTIMEOUT(fun, arg, handle) untimeout(fun, arg, handle) # define TIMEOUT(fun, arg1, arg2, handle) handle = timeout(fun, arg1, arg2) # define IOCTL_CMD_T u_long #else # define UNTIMEOUT(fun, arg, handle) untimeout(fun, arg) # define TIMEOUT(fun, arg1, arg2, handle) timeout(fun, arg1, arg2) # define IOCTL_CMD_T int #endif #define MAXALIVECNT 3 /* max. alive packets */ /* * Interface flags that can be set in an ifconfig command. * * Setting link0 will make the link passive, i.e. it will be marked * as being administrative openable, but won't be opened to begin * with. Incoming calls will be answered, or subsequent calls with * -link1 will cause the administrative open of the LCP layer. * * Setting link1 will cause the link to auto-dial only as packets * arrive to be sent. * * Setting IFF_DEBUG will syslog the option negotiation and state * transitions at level kern.debug. Note: all logs consistently look * like * * : * * with being something like "bppp0", and * being one of "lcp", "ipcp", "cisco", "chap", "pap", etc. */ #define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ #define IFF_AUTO IFF_LINK1 /* auto-dial on output */ #define IFF_CISCO IFF_LINK2 /* auto-dial on output */ #define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ #define PPP_UI 0x03 /* Unnumbered Information */ #define PPP_IP 0x0021 /* Internet Protocol */ #define PPP_ISO 0x0023 /* ISO OSI Protocol */ #define PPP_XNS 0x0025 /* Xerox NS Protocol */ #define PPP_IPX 0x002b /* Novell IPX Protocol */ #define PPP_LCP 0xc021 /* Link Control Protocol */ #define PPP_PAP 0xc023 /* Password Authentication Protocol */ #define PPP_CHAP 0xc223 /* Challenge-Handshake Auth Protocol */ #define PPP_IPCP 0x8021 /* Internet Protocol Control Protocol */ #define CONF_REQ 1 /* PPP configure request */ #define CONF_ACK 2 /* PPP configure acknowledge */ #define CONF_NAK 3 /* PPP configure negative ack */ #define CONF_REJ 4 /* PPP configure reject */ #define TERM_REQ 5 /* PPP terminate request */ #define TERM_ACK 6 /* PPP terminate acknowledge */ #define CODE_REJ 7 /* PPP code reject */ #define PROTO_REJ 8 /* PPP protocol reject */ #define ECHO_REQ 9 /* PPP echo request */ #define ECHO_REPLY 10 /* PPP echo reply */ #define DISC_REQ 11 /* PPP discard request */ #define LCP_OPT_MRU 1 /* maximum receive unit */ #define LCP_OPT_ASYNC_MAP 2 /* async control character map */ #define LCP_OPT_AUTH_PROTO 3 /* authentication protocol */ #define LCP_OPT_QUAL_PROTO 4 /* quality protocol */ #define LCP_OPT_MAGIC 5 /* magic number */ #define LCP_OPT_RESERVED 6 /* reserved */ #define LCP_OPT_PROTO_COMP 7 /* protocol field compression */ #define LCP_OPT_ADDR_COMP 8 /* address/control field compression */ #define IPCP_OPT_ADDRESSES 1 /* both IP addresses; deprecated */ #define IPCP_OPT_COMPRESSION 2 /* IP compression protocol (VJ) */ #define IPCP_OPT_ADDRESS 3 /* local IP address */ #define PAP_REQ 1 /* PAP name/password request */ #define PAP_ACK 2 /* PAP acknowledge */ #define PAP_NAK 3 /* PAP fail */ #define CHAP_CHALLENGE 1 /* CHAP challenge request */ #define CHAP_RESPONSE 2 /* CHAP challenge response */ #define CHAP_SUCCESS 3 /* CHAP response ok */ #define CHAP_FAILURE 4 /* CHAP response failed */ #define CHAP_MD5 5 /* hash algorithm - MD5 */ #define CISCO_MULTICAST 0x8f /* Cisco multicast address */ #define CISCO_UNICAST 0x0f /* Cisco unicast address */ #define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ #define CISCO_ADDR_REQ 0 /* Cisco address request */ #define CISCO_ADDR_REPLY 1 /* Cisco address reply */ #define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ /* states are named and numbered according to RFC 1661 */ #define STATE_INITIAL 0 #define STATE_STARTING 1 #define STATE_CLOSED 2 #define STATE_STOPPED 3 #define STATE_CLOSING 4 #define STATE_STOPPING 5 #define STATE_REQ_SENT 6 #define STATE_ACK_RCVD 7 #define STATE_ACK_SENT 8 #define STATE_OPENED 9 struct ppp_header { u_char address; u_char control; u_short protocol; }; #define PPP_HEADER_LEN sizeof (struct ppp_header) struct lcp_header { u_char type; u_char ident; u_short len; }; #define LCP_HEADER_LEN sizeof (struct lcp_header) struct cisco_packet { u_long type; u_long par1; u_long par2; u_short rel; u_short time0; u_short time1; }; #define CISCO_PACKET_LEN 18 /* * We follow the spelling and capitalization of RFC 1661 here, to make * it easier comparing with the standard. Please refer to this RFC in * case you can't make sense out of these abbreviation; it will also * explain the semantics related to the various events and actions. */ struct cp { u_short proto; /* PPP control protocol number */ u_char protoidx; /* index into state table in struct sppp */ u_char flags; #define CP_LCP 0x01 /* this is the LCP */ #define CP_AUTH 0x02 /* this is an authentication protocol */ #define CP_NCP 0x04 /* this is a NCP */ #define CP_QUAL 0x08 /* this is a quality reporting protocol */ const char *name; /* name of this control protocol */ /* event handlers */ void (*Up)(struct sppp *sp); void (*Down)(struct sppp *sp); void (*Open)(struct sppp *sp); void (*Close)(struct sppp *sp); void (*TO)(void *sp); int (*RCR)(struct sppp *sp, struct lcp_header *h, int len); void (*RCN_rej)(struct sppp *sp, struct lcp_header *h, int len); void (*RCN_nak)(struct sppp *sp, struct lcp_header *h, int len); /* actions */ void (*tlu)(struct sppp *sp); void (*tld)(struct sppp *sp); void (*tls)(struct sppp *sp); void (*tlf)(struct sppp *sp); void (*scr)(struct sppp *sp); }; static struct sppp *spppq; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 static struct callout_handle keepalive_ch; #endif #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #define SPP_FMT "%s%d: " #define SPP_ARGS(ifp) (ifp)->if_name, (ifp)->if_unit #else #define SPP_FMT "%s: " #define SPP_ARGS(ifp) (ifp)->if_xname #endif /* * The following disgusting hack gets around the problem that IP TOS * can't be set yet. We want to put "interactive" traffic on a high * priority queue. To decide if traffic is interactive, we check that * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control. * * XXX is this really still necessary? - joerg - */ static u_short interactive_ports[8] = { 0, 513, 0, 0, 0, 21, 0, 23, }; #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) /* almost every function needs these */ #define STDDCL \ struct ifnet *ifp = &sp->pp_if; \ int debug = ifp->if_flags & IFF_DEBUG static int sppp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt); static void sppp_cisco_send(struct sppp *sp, int type, long par1, long par2); static void sppp_cisco_input(struct sppp *sp, struct mbuf *m); static void sppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m); static void sppp_cp_send(struct sppp *sp, u_short proto, u_char type, u_char ident, u_short len, void *data); /* static void sppp_cp_timeout(void *arg); */ static void sppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate); static void sppp_auth_send(const struct cp *cp, struct sppp *sp, unsigned int type, unsigned int id, ...); static void sppp_up_event(const struct cp *cp, struct sppp *sp); static void sppp_down_event(const struct cp *cp, struct sppp *sp); static void sppp_open_event(const struct cp *cp, struct sppp *sp); static void sppp_close_event(const struct cp *cp, struct sppp *sp); static void sppp_to_event(const struct cp *cp, struct sppp *sp); static void sppp_null(struct sppp *sp); static void sppp_lcp_init(struct sppp *sp); static void sppp_lcp_up(struct sppp *sp); static void sppp_lcp_down(struct sppp *sp); static void sppp_lcp_open(struct sppp *sp); static void sppp_lcp_close(struct sppp *sp); static void sppp_lcp_TO(void *sp); static int sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len); static void sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); static void sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); static void sppp_lcp_tlu(struct sppp *sp); static void sppp_lcp_tld(struct sppp *sp); static void sppp_lcp_tls(struct sppp *sp); static void sppp_lcp_tlf(struct sppp *sp); static void sppp_lcp_scr(struct sppp *sp); static void sppp_lcp_check_and_close(struct sppp *sp); static int sppp_ncp_check(struct sppp *sp); static void sppp_ipcp_init(struct sppp *sp); static void sppp_ipcp_up(struct sppp *sp); static void sppp_ipcp_down(struct sppp *sp); static void sppp_ipcp_open(struct sppp *sp); static void sppp_ipcp_close(struct sppp *sp); static void sppp_ipcp_TO(void *sp); static int sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len); static void sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); static void sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); static void sppp_ipcp_tlu(struct sppp *sp); static void sppp_ipcp_tld(struct sppp *sp); static void sppp_ipcp_tls(struct sppp *sp); static void sppp_ipcp_tlf(struct sppp *sp); static void sppp_ipcp_scr(struct sppp *sp); static void sppp_pap_input(struct sppp *sp, struct mbuf *m); static void sppp_pap_init(struct sppp *sp); static void sppp_pap_open(struct sppp *sp); static void sppp_pap_close(struct sppp *sp); static void sppp_pap_TO(void *sp); static void sppp_pap_my_TO(void *sp); static void sppp_pap_tlu(struct sppp *sp); static void sppp_pap_tld(struct sppp *sp); static void sppp_pap_scr(struct sppp *sp); static void sppp_chap_input(struct sppp *sp, struct mbuf *m); static void sppp_chap_init(struct sppp *sp); static void sppp_chap_open(struct sppp *sp); static void sppp_chap_close(struct sppp *sp); static void sppp_chap_TO(void *sp); static void sppp_chap_tlu(struct sppp *sp); static void sppp_chap_tld(struct sppp *sp); static void sppp_chap_scr(struct sppp *sp); static const char *sppp_auth_type_name(u_short proto, u_char type); static const char *sppp_cp_type_name(u_char type); static const char *sppp_dotted_quad(u_long addr); static const char *sppp_ipcp_opt_name(u_char opt); static const char *sppp_lcp_opt_name(u_char opt); static const char *sppp_phase_name(enum ppp_phase phase); static const char *sppp_proto_name(u_short proto); static const char *sppp_state_name(int state); static int sppp_params(struct sppp *sp, u_long cmd, void *data); static int sppp_strnlen(u_char *p, int max); static void sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, u_long *srcmask); static void sppp_keepalive(void *dummy); static void sppp_phase_network(struct sppp *sp); static void sppp_print_bytes(const u_char *p, u_short len); static void sppp_print_string(const char *p, u_short len); static void sppp_qflush(struct ifqueue *ifq); static void sppp_set_ip_addr(struct sppp *sp, u_long src); /* our control protocol descriptors */ static const struct cp lcp = { PPP_LCP, IDX_LCP, CP_LCP, "lcp", sppp_lcp_up, sppp_lcp_down, sppp_lcp_open, sppp_lcp_close, sppp_lcp_TO, sppp_lcp_RCR, sppp_lcp_RCN_rej, sppp_lcp_RCN_nak, sppp_lcp_tlu, sppp_lcp_tld, sppp_lcp_tls, sppp_lcp_tlf, sppp_lcp_scr }; static const struct cp ipcp = { PPP_IPCP, IDX_IPCP, CP_NCP, "ipcp", sppp_ipcp_up, sppp_ipcp_down, sppp_ipcp_open, sppp_ipcp_close, sppp_ipcp_TO, sppp_ipcp_RCR, sppp_ipcp_RCN_rej, sppp_ipcp_RCN_nak, sppp_ipcp_tlu, sppp_ipcp_tld, sppp_ipcp_tls, sppp_ipcp_tlf, sppp_ipcp_scr }; static const struct cp pap = { PPP_PAP, IDX_PAP, CP_AUTH, "pap", sppp_null, sppp_null, sppp_pap_open, sppp_pap_close, sppp_pap_TO, 0, 0, 0, sppp_pap_tlu, sppp_pap_tld, sppp_null, sppp_null, sppp_pap_scr }; static const struct cp chap = { PPP_CHAP, IDX_CHAP, CP_AUTH, "chap", sppp_null, sppp_null, sppp_chap_open, sppp_chap_close, sppp_chap_TO, 0, 0, 0, sppp_chap_tlu, sppp_chap_tld, sppp_null, sppp_null, sppp_chap_scr }; static const struct cp *cps[IDX_COUNT] = { &lcp, /* IDX_LCP */ &ipcp, /* IDX_IPCP */ &pap, /* IDX_PAP */ &chap, /* IDX_CHAP */ }; static int sppp_modevent(module_t mod, int type, void *unused) { switch (type) { case MOD_LOAD: break; case MOD_UNLOAD: return EACCES; break; default: break; } return 0; } static moduledata_t spppmod = { "sppp", sppp_modevent, 0 }; MODULE_VERSION(sppp, 1); DECLARE_MODULE(sppp, spppmod, SI_SUB_DRIVERS, SI_ORDER_ANY); /* * Exported functions, comprising our interface to the lower layer. */ /* * Process the received packet. */ void sppp_input(struct ifnet *ifp, struct mbuf *m) { struct ppp_header *h; struct ifqueue *inq = 0; struct sppp *sp = (struct sppp *)ifp; int debug = ifp->if_flags & IFF_DEBUG; if (ifp->if_flags & IFF_UP) /* Count received bytes, add FCS and one flag */ ifp->if_ibytes += m->m_pkthdr.len + 3; if (m->m_pkthdr.len <= PPP_HEADER_LEN) { /* Too small packet, drop it. */ if (debug) log(LOG_DEBUG, SPP_FMT "input packet is too small, %d bytes\n", SPP_ARGS(ifp), m->m_pkthdr.len); drop: ++ifp->if_ierrors; ++ifp->if_iqdrops; m_freem (m); return; } /* Get PPP header. */ h = mtod (m, struct ppp_header*); m_adj (m, PPP_HEADER_LEN); switch (h->address) { case PPP_ALLSTATIONS: if (h->control != PPP_UI) goto invalid; if (sp->pp_mode == IFF_CISCO) { if (debug) log(LOG_DEBUG, SPP_FMT "PPP packet in Cisco mode " "\n", SPP_ARGS(ifp), h->address, h->control, ntohs(h->protocol)); goto drop; } switch (ntohs (h->protocol)) { default: if (debug) log(LOG_DEBUG, SPP_FMT "rejecting protocol " "\n", SPP_ARGS(ifp), h->address, h->control, ntohs(h->protocol)); if (sp->state[IDX_LCP] == STATE_OPENED) sppp_cp_send (sp, PPP_LCP, PROTO_REJ, ++sp->pp_seq, m->m_pkthdr.len + 2, &h->protocol); ++ifp->if_noproto; goto drop; case PPP_LCP: sppp_cp_input(&lcp, sp, m); m_freem (m); return; case PPP_PAP: if (sp->pp_phase >= PHASE_AUTHENTICATE) sppp_pap_input(sp, m); m_freem (m); return; case PPP_CHAP: if (sp->pp_phase >= PHASE_AUTHENTICATE) sppp_chap_input(sp, m); m_freem (m); return; #ifdef INET case PPP_IPCP: if (sp->pp_phase == PHASE_NETWORK) sppp_cp_input(&ipcp, sp, m); m_freem (m); return; case PPP_IP: if (sp->state[IDX_IPCP] == STATE_OPENED) { schednetisr (NETISR_IP); inq = &ipintrq; } break; #endif #ifdef IPX case PPP_IPX: /* IPX IPXCP not implemented yet */ if (sp->pp_phase == PHASE_NETWORK) { schednetisr (NETISR_IPX); inq = &ipxintrq; } break; #endif #ifdef NS case PPP_XNS: /* XNS IDPCP not implemented yet */ if (sp->pp_phase == PHASE_NETWORK) { schednetisr (NETISR_NS); inq = &nsintrq; } break; #endif } break; case CISCO_MULTICAST: case CISCO_UNICAST: /* Don't check the control field here (RFC 1547). */ if (sp->pp_mode != IFF_CISCO) { if (debug) log(LOG_DEBUG, SPP_FMT "Cisco packet in PPP mode " "\n", SPP_ARGS(ifp), h->address, h->control, ntohs(h->protocol)); goto drop; } switch (ntohs (h->protocol)) { default: ++ifp->if_noproto; goto invalid; case CISCO_KEEPALIVE: sppp_cisco_input ((struct sppp*) ifp, m); m_freem (m); return; #ifdef INET case ETHERTYPE_IP: schednetisr (NETISR_IP); inq = &ipintrq; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: schednetisr (NETISR_IPV6); inq = &ip6intrq; break; #endif #ifdef IPX case ETHERTYPE_IPX: schednetisr (NETISR_IPX); inq = &ipxintrq; break; #endif #ifdef NS case ETHERTYPE_NS: schednetisr (NETISR_NS); inq = &nsintrq; break; #endif } break; default: /* Invalid PPP packet. */ invalid: if (debug) log(LOG_DEBUG, SPP_FMT "invalid input packet " "\n", SPP_ARGS(ifp), h->address, h->control, ntohs(h->protocol)); goto drop; } if (! (ifp->if_flags & IFF_UP) || ! inq) goto drop; /* Check queue. */ if (! IF_HANDOFF(inq, m, NULL)) { if (debug) log(LOG_DEBUG, SPP_FMT "protocol queue overflow\n", SPP_ARGS(ifp)); goto drop; } } /* * Enqueue transmit packet. */ static int sppp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt) { struct sppp *sp = (struct sppp*) ifp; struct ppp_header *h; struct ifqueue *ifq; int s, rv = 0; int debug = ifp->if_flags & IFF_DEBUG; s = splimp(); if ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == 0) { m_freem (m); splx (s); return (ENETDOWN); } if ((ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == IFF_AUTO) { /* * Interface is not yet running, but auto-dial. Need * to start LCP for it. */ ifp->if_flags |= IFF_RUNNING; splx(s); lcp.Open(sp); s = splimp(); } ifq = &ifp->if_snd; #ifdef INET if (dst->sa_family == AF_INET) { /* XXX Check mbuf length here? */ struct ip *ip = mtod (m, struct ip*); struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl); /* * When using dynamic local IP address assignment by using * 0.0.0.0 as a local address, the first TCP session will * not connect because the local TCP checksum is computed * using 0.0.0.0 which will later become our real IP address * so the TCP checksum computed at the remote end will * become invalid. So we * - don't let packets with src ip addr 0 thru * - we flag TCP packets with src ip 0 as an error */ if(ip->ip_src.s_addr == INADDR_ANY) /* -hm */ { m_freem(m); splx(s); if(ip->ip_p == IPPROTO_TCP) return(EADDRNOTAVAIL); else return(0); } /* * Put low delay, telnet, rlogin and ftp control packets * in front of the queue. */ if (_IF_QFULL(&sp->pp_fastq)) ; else if (ip->ip_tos & IPTOS_LOWDELAY) ifq = &sp->pp_fastq; else if (m->m_len < sizeof *ip + sizeof *tcp) ; else if (ip->ip_p != IPPROTO_TCP) ; else if (INTERACTIVE (ntohs (tcp->th_sport))) ifq = &sp->pp_fastq; else if (INTERACTIVE (ntohs (tcp->th_dport))) ifq = &sp->pp_fastq; } #endif /* * Prepend general data packet PPP header. For now, IP only. */ M_PREPEND (m, PPP_HEADER_LEN, M_DONTWAIT); if (! m) { if (debug) log(LOG_DEBUG, SPP_FMT "no memory for transmit header\n", SPP_ARGS(ifp)); ++ifp->if_oerrors; splx (s); return (ENOBUFS); } /* * May want to check size of packet * (albeit due to the implementation it's always enough) */ h = mtod (m, struct ppp_header*); if (sp->pp_mode == IFF_CISCO) { h->address = CISCO_UNICAST; /* unicast address */ h->control = 0; } else { h->address = PPP_ALLSTATIONS; /* broadcast address */ h->control = PPP_UI; /* Unnumbered Info */ } switch (dst->sa_family) { #ifdef INET case AF_INET: /* Internet Protocol */ if (sp->pp_mode == IFF_CISCO) h->protocol = htons (ETHERTYPE_IP); else { /* * Don't choke with an ENETDOWN early. It's * possible that we just started dialing out, * so don't drop the packet immediately. If * we notice that we run out of buffer space * below, we will however remember that we are * not ready to carry IP packets, and return * ENETDOWN, as opposed to ENOBUFS. */ h->protocol = htons(PPP_IP); if (sp->state[IDX_IPCP] != STATE_OPENED) rv = ENETDOWN; } break; #endif #ifdef INET6 case AF_INET6: /* Internet Protocol */ if (sp->pp_mode == IFF_CISCO) h->protocol = htons (ETHERTYPE_IPV6); else { goto nosupport; } break; #endif #ifdef NS case AF_NS: /* Xerox NS Protocol */ h->protocol = htons (sp->pp_mode == IFF_CISCO ? ETHERTYPE_NS : PPP_XNS); break; #endif #ifdef IPX case AF_IPX: /* Novell IPX Protocol */ h->protocol = htons (sp->pp_mode == IFF_CISCO ? ETHERTYPE_IPX : PPP_IPX); break; #endif nosupport: default: m_freem (m); ++ifp->if_oerrors; splx (s); return (EAFNOSUPPORT); } /* * Queue message on interface, and start output if interface * not yet active. Also adjust output byte count. * The packet length includes header, FCS and 1 flag, * according to RFC 1333. */ if (! IF_HANDOFF_ADJ(ifq, m, ifp, 3)) { ++ifp->if_oerrors; return (rv? rv: ENOBUFS); } return (0); } void sppp_attach(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; /* Initialize keepalive handler. */ if (! spppq) TIMEOUT(sppp_keepalive, 0, hz * 10, keepalive_ch); /* Insert new entry into the keepalive list. */ sp->pp_next = spppq; spppq = sp; sp->pp_if.if_mtu = PP_MTU; sp->pp_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; sp->pp_if.if_type = IFT_PPP; sp->pp_if.if_output = sppp_output; #if 0 sp->pp_flags = PP_KEEPALIVE; #endif sp->pp_if.if_snd.ifq_maxlen = 32; sp->pp_fastq.ifq_maxlen = 32; sp->pp_cpq.ifq_maxlen = 20; sp->pp_loopcnt = 0; sp->pp_alivecnt = 0; sp->pp_seq = 0; sp->pp_rseq = 0; sp->pp_phase = PHASE_DEAD; sp->pp_up = lcp.Up; sp->pp_down = lcp.Down; mtx_init(&sp->pp_cpq.ifq_mtx, "sppp_cpq", MTX_DEF); mtx_init(&sp->pp_fastq.ifq_mtx, "sppp_fastq", MTX_DEF); sppp_lcp_init(sp); sppp_ipcp_init(sp); sppp_pap_init(sp); sppp_chap_init(sp); } void sppp_detach(struct ifnet *ifp) { struct sppp **q, *p, *sp = (struct sppp*) ifp; int i; /* Remove the entry from the keepalive list. */ for (q = &spppq; (p = *q); q = &p->pp_next) if (p == sp) { *q = p->pp_next; break; } /* Stop keepalive handler. */ if (! spppq) UNTIMEOUT(sppp_keepalive, 0, keepalive_ch); for (i = 0; i < IDX_COUNT; i++) UNTIMEOUT((cps[i])->TO, (void *)sp, sp->ch[i]); UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); mtx_destroy(&sp->pp_cpq.ifq_mtx); mtx_destroy(&sp->pp_fastq.ifq_mtx); } /* * Flush the interface output queue. */ void sppp_flush(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; sppp_qflush (&sp->pp_if.if_snd); sppp_qflush (&sp->pp_fastq); sppp_qflush (&sp->pp_cpq); } /* * Check if the output queue is empty. */ int sppp_isempty(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; int empty, s; s = splimp(); empty = !sp->pp_fastq.ifq_head && !sp->pp_cpq.ifq_head && !sp->pp_if.if_snd.ifq_head; splx(s); return (empty); } /* * Get next packet to send. */ struct mbuf * sppp_dequeue(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; struct mbuf *m; int s; s = splimp(); /* * Process only the control protocol queue until we have at * least one NCP open. * * Do always serve all three queues in Cisco mode. */ IF_DEQUEUE(&sp->pp_cpq, m); if (m == NULL && (sppp_ncp_check(sp) || sp->pp_mode == IFF_CISCO)) { IF_DEQUEUE(&sp->pp_fastq, m); if (m == NULL) IF_DEQUEUE (&sp->pp_if.if_snd, m); } splx(s); return m; } /* * Pick the next packet, do not remove it from the queue. */ struct mbuf * sppp_pick(struct ifnet *ifp) { struct sppp *sp = (struct sppp*)ifp; struct mbuf *m; int s; s= splimp (); m = sp->pp_cpq.ifq_head; if (m == NULL && (sp->pp_phase == PHASE_NETWORK || sp->pp_mode == IFF_CISCO)) if ((m = sp->pp_fastq.ifq_head) == NULL) m = sp->pp_if.if_snd.ifq_head; splx (s); return (m); } /* * Process an ioctl request. Called on low priority level. */ int sppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, void *data) { struct ifreq *ifr = (struct ifreq*) data; struct sppp *sp = (struct sppp*) ifp; int s, rv, going_up, going_down, newmode; s = splimp(); rv = 0; switch (cmd) { case SIOCAIFADDR: case SIOCSIFDSTADDR: break; case SIOCSIFADDR: if_up(ifp); /* fall through... */ case SIOCSIFFLAGS: going_up = ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0; going_down = (ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING; newmode = ifp->if_flags & IFF_PASSIVE; if (!newmode) newmode = ifp->if_flags & IFF_AUTO; if (!newmode) newmode = ifp->if_flags & IFF_CISCO; ifp->if_flags &= ~(IFF_PASSIVE | IFF_AUTO | IFF_CISCO); ifp->if_flags |= newmode; if (newmode != sp->pp_mode) { going_down = 1; if (!going_up) going_up = ifp->if_flags & IFF_RUNNING; } if (going_down) { if (sp->pp_mode != IFF_CISCO) lcp.Close(sp); else if (sp->pp_tlf) (sp->pp_tlf)(sp); sppp_flush(ifp); ifp->if_flags &= ~IFF_RUNNING; sp->pp_mode = newmode; } if (going_up) { if (sp->pp_mode != IFF_CISCO) lcp.Close(sp); sp->pp_mode = newmode; if (sp->pp_mode == 0) { ifp->if_flags |= IFF_RUNNING; lcp.Open(sp); } if (sp->pp_mode == IFF_CISCO) { if (sp->pp_tls) (sp->pp_tls)(sp); ifp->if_flags |= IFF_RUNNING; } } break; #ifdef SIOCSIFMTU #ifndef ifr_mtu #define ifr_mtu ifr_metric #endif case SIOCSIFMTU: if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > sp->lcp.their_mru) return (EINVAL); ifp->if_mtu = ifr->ifr_mtu; break; #endif #ifdef SLIOCSETMTU case SLIOCSETMTU: if (*(short*)data < 128 || *(short*)data > sp->lcp.their_mru) return (EINVAL); ifp->if_mtu = *(short*)data; break; #endif #ifdef SIOCGIFMTU case SIOCGIFMTU: ifr->ifr_mtu = ifp->if_mtu; break; #endif #ifdef SLIOCGETMTU case SLIOCGETMTU: *(short*)data = ifp->if_mtu; break; #endif case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCGIFGENERIC: case SIOCSIFGENERIC: rv = sppp_params(sp, cmd, data); break; default: rv = ENOTTY; } splx(s); return rv; } /* * Cisco framing implementation. */ /* * Handle incoming Cisco keepalive protocol packets. */ static void sppp_cisco_input(struct sppp *sp, struct mbuf *m) { STDDCL; struct cisco_packet *h; u_long me, mymask; if (m->m_pkthdr.len < CISCO_PACKET_LEN) { if (debug) log(LOG_DEBUG, SPP_FMT "cisco invalid packet length: %d bytes\n", SPP_ARGS(ifp), m->m_pkthdr.len); return; } h = mtod (m, struct cisco_packet*); if (debug) log(LOG_DEBUG, SPP_FMT "cisco input: %d bytes " "<0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n", SPP_ARGS(ifp), m->m_pkthdr.len, (u_long)ntohl (h->type), (u_long)h->par1, (u_long)h->par2, (u_int)h->rel, (u_int)h->time0, (u_int)h->time1); switch (ntohl (h->type)) { default: if (debug) log(-1, SPP_FMT "cisco unknown packet type: 0x%lx\n", SPP_ARGS(ifp), (u_long)ntohl (h->type)); break; case CISCO_ADDR_REPLY: /* Reply on address request, ignore */ break; case CISCO_KEEPALIVE_REQ: sp->pp_alivecnt = 0; sp->pp_rseq = ntohl (h->par1); if (sp->pp_seq == sp->pp_rseq) { /* Local and remote sequence numbers are equal. * Probably, the line is in loopback mode. */ if (sp->pp_loopcnt >= MAXALIVECNT) { printf (SPP_FMT "loopback\n", SPP_ARGS(ifp)); sp->pp_loopcnt = 0; if (ifp->if_flags & IFF_UP) { if_down (ifp); sppp_qflush (&sp->pp_cpq); } } ++sp->pp_loopcnt; /* Generate new local sequence number */ #if defined(__FreeBSD__) && __FreeBSD__ >= 3 sp->pp_seq = random(); #else sp->pp_seq ^= time.tv_sec ^ time.tv_usec; #endif break; } sp->pp_loopcnt = 0; if (! (ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING)) { if_up(ifp); printf (SPP_FMT "up\n", SPP_ARGS(ifp)); } break; case CISCO_ADDR_REQ: sppp_get_ip_addrs(sp, &me, 0, &mymask); if (me != 0L) sppp_cisco_send(sp, CISCO_ADDR_REPLY, me, mymask); break; } } /* * Send Cisco keepalive packet. */ static void sppp_cisco_send(struct sppp *sp, int type, long par1, long par2) { STDDCL; struct ppp_header *h; struct cisco_packet *ch; struct mbuf *m; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 struct timeval tv; #else u_long t = (time.tv_sec - boottime.tv_sec) * 1000; #endif #if defined(__FreeBSD__) && __FreeBSD__ >= 3 getmicrouptime(&tv); #endif MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return; m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN; m->m_pkthdr.rcvif = 0; h = mtod (m, struct ppp_header*); h->address = CISCO_MULTICAST; h->control = 0; h->protocol = htons (CISCO_KEEPALIVE); ch = (struct cisco_packet*) (h + 1); ch->type = htonl (type); ch->par1 = htonl (par1); ch->par2 = htonl (par2); ch->rel = -1; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 ch->time0 = htons ((u_short) (tv.tv_sec >> 16)); ch->time1 = htons ((u_short) tv.tv_sec); #else ch->time0 = htons ((u_short) (t >> 16)); ch->time1 = htons ((u_short) t); #endif if (debug) log(LOG_DEBUG, SPP_FMT "cisco output: <0x%lx 0x%lx 0x%lx 0x%x 0x%x-0x%x>\n", SPP_ARGS(ifp), (u_long)ntohl (ch->type), (u_long)ch->par1, (u_long)ch->par2, (u_int)ch->rel, (u_int)ch->time0, (u_int)ch->time1); if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3)) ifp->if_oerrors++; } /* * PPP protocol implementation. */ /* * Send PPP control protocol packet. */ static void sppp_cp_send(struct sppp *sp, u_short proto, u_char type, u_char ident, u_short len, void *data) { STDDCL; struct ppp_header *h; struct lcp_header *lh; struct mbuf *m; if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN; MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return; m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len; m->m_pkthdr.rcvif = 0; h = mtod (m, struct ppp_header*); h->address = PPP_ALLSTATIONS; /* broadcast address */ h->control = PPP_UI; /* Unnumbered Info */ h->protocol = htons (proto); /* Link Control Protocol */ lh = (struct lcp_header*) (h + 1); lh->type = type; lh->ident = ident; lh->len = htons (LCP_HEADER_LEN + len); if (len) bcopy (data, lh+1, len); if (debug) { log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d", SPP_ARGS(ifp), sppp_proto_name(proto), sppp_cp_type_name (lh->type), lh->ident, ntohs (lh->len)); sppp_print_bytes ((u_char*) (lh+1), len); log(-1, ">\n"); } if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3)) ifp->if_oerrors++; } /* * Handle incoming PPP control protocol packets. */ static void sppp_cp_input(const struct cp *cp, struct sppp *sp, struct mbuf *m) { STDDCL; struct lcp_header *h; int len = m->m_pkthdr.len; int rv; u_char *p; if (len < 4) { if (debug) log(LOG_DEBUG, SPP_FMT "%s invalid packet length: %d bytes\n", SPP_ARGS(ifp), cp->name, len); return; } h = mtod (m, struct lcp_header*); if (debug) { log(LOG_DEBUG, SPP_FMT "%s input(%s): <%s id=0x%x len=%d", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx]), sppp_cp_type_name (h->type), h->ident, ntohs (h->len)); sppp_print_bytes ((u_char*) (h+1), len-4); log(-1, ">\n"); } if (len > ntohs (h->len)) len = ntohs (h->len); p = (u_char *)(h + 1); switch (h->type) { case CONF_REQ: if (len < 4) { if (debug) log(-1, SPP_FMT "%s invalid conf-req length %d\n", SPP_ARGS(ifp), cp->name, len); ++ifp->if_ierrors; break; } /* handle states where RCR doesn't get a SCA/SCN */ switch (sp->state[cp->protoidx]) { case STATE_CLOSING: case STATE_STOPPING: return; case STATE_CLOSED: sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); return; } rv = (cp->RCR)(sp, h, len); switch (sp->state[cp->protoidx]) { case STATE_OPENED: (cp->tld)(sp); (cp->scr)(sp); /* fall through... */ case STATE_ACK_SENT: case STATE_REQ_SENT: /* * sppp_cp_change_state() have the side effect of * restarting the timeouts. We want to avoid that * if the state don't change, otherwise we won't * ever timeout and resend a configuration request * that got lost. */ if (sp->state[cp->protoidx] == (rv ? STATE_ACK_SENT: STATE_REQ_SENT)) break; sppp_cp_change_state(cp, sp, rv? STATE_ACK_SENT: STATE_REQ_SENT); break; case STATE_STOPPED: sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; (cp->scr)(sp); sppp_cp_change_state(cp, sp, rv? STATE_ACK_SENT: STATE_REQ_SENT); break; case STATE_ACK_RCVD: if (rv) { sppp_cp_change_state(cp, sp, STATE_OPENED); if (debug) log(LOG_DEBUG, SPP_FMT "%s tlu\n", SPP_ARGS(ifp), cp->name); (cp->tlu)(sp); } else sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); break; default: printf(SPP_FMT "%s illegal %s in state %s\n", SPP_ARGS(ifp), cp->name, sppp_cp_type_name(h->type), sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } break; case CONF_ACK: if (h->ident != sp->confid[cp->protoidx]) { if (debug) log(-1, SPP_FMT "%s id mismatch 0x%x != 0x%x\n", SPP_ARGS(ifp), cp->name, h->ident, sp->confid[cp->protoidx]); ++ifp->if_ierrors; break; } switch (sp->state[cp->protoidx]) { case STATE_CLOSED: case STATE_STOPPED: sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); break; case STATE_CLOSING: case STATE_STOPPING: break; case STATE_REQ_SENT: sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); break; case STATE_OPENED: (cp->tld)(sp); /* fall through */ case STATE_ACK_RCVD: (cp->scr)(sp); sppp_cp_change_state(cp, sp, STATE_REQ_SENT); break; case STATE_ACK_SENT: sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; sppp_cp_change_state(cp, sp, STATE_OPENED); if (debug) log(LOG_DEBUG, SPP_FMT "%s tlu\n", SPP_ARGS(ifp), cp->name); (cp->tlu)(sp); break; default: printf(SPP_FMT "%s illegal %s in state %s\n", SPP_ARGS(ifp), cp->name, sppp_cp_type_name(h->type), sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } break; case CONF_NAK: case CONF_REJ: if (h->ident != sp->confid[cp->protoidx]) { if (debug) log(-1, SPP_FMT "%s id mismatch 0x%x != 0x%x\n", SPP_ARGS(ifp), cp->name, h->ident, sp->confid[cp->protoidx]); ++ifp->if_ierrors; break; } if (h->type == CONF_NAK) (cp->RCN_nak)(sp, h, len); else /* CONF_REJ */ (cp->RCN_rej)(sp, h, len); switch (sp->state[cp->protoidx]) { case STATE_CLOSED: case STATE_STOPPED: sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); break; case STATE_REQ_SENT: case STATE_ACK_SENT: sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; /* * Slow things down a bit if we think we might be * in loopback. Depend on the timeout to send the * next configuration request. */ if (sp->pp_loopcnt) break; (cp->scr)(sp); break; case STATE_OPENED: (cp->tld)(sp); /* fall through */ case STATE_ACK_RCVD: sppp_cp_change_state(cp, sp, STATE_REQ_SENT); (cp->scr)(sp); break; case STATE_CLOSING: case STATE_STOPPING: break; default: printf(SPP_FMT "%s illegal %s in state %s\n", SPP_ARGS(ifp), cp->name, sppp_cp_type_name(h->type), sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } break; case TERM_REQ: switch (sp->state[cp->protoidx]) { case STATE_ACK_RCVD: case STATE_ACK_SENT: sppp_cp_change_state(cp, sp, STATE_REQ_SENT); /* fall through */ case STATE_CLOSED: case STATE_STOPPED: case STATE_CLOSING: case STATE_STOPPING: case STATE_REQ_SENT: sta: /* Send Terminate-Ack packet. */ if (debug) log(LOG_DEBUG, SPP_FMT "%s send terminate-ack\n", SPP_ARGS(ifp), cp->name); sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); break; case STATE_OPENED: (cp->tld)(sp); sp->rst_counter[cp->protoidx] = 0; sppp_cp_change_state(cp, sp, STATE_STOPPING); goto sta; break; default: printf(SPP_FMT "%s illegal %s in state %s\n", SPP_ARGS(ifp), cp->name, sppp_cp_type_name(h->type), sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } break; case TERM_ACK: switch (sp->state[cp->protoidx]) { case STATE_CLOSED: case STATE_STOPPED: case STATE_REQ_SENT: case STATE_ACK_SENT: break; case STATE_CLOSING: sppp_cp_change_state(cp, sp, STATE_CLOSED); (cp->tlf)(sp); break; case STATE_STOPPING: sppp_cp_change_state(cp, sp, STATE_STOPPED); (cp->tlf)(sp); break; case STATE_ACK_RCVD: sppp_cp_change_state(cp, sp, STATE_REQ_SENT); break; case STATE_OPENED: (cp->tld)(sp); (cp->scr)(sp); sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); break; default: printf(SPP_FMT "%s illegal %s in state %s\n", SPP_ARGS(ifp), cp->name, sppp_cp_type_name(h->type), sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } break; case CODE_REJ: case PROTO_REJ: /* XXX catastrophic rejects (RXJ-) aren't handled yet. */ log(LOG_INFO, SPP_FMT "%s: ignoring RXJ (%s) for proto 0x%x, " "danger will robinson\n", SPP_ARGS(ifp), cp->name, sppp_cp_type_name(h->type), ntohs(*((u_short *)p))); switch (sp->state[cp->protoidx]) { case STATE_CLOSED: case STATE_STOPPED: case STATE_REQ_SENT: case STATE_ACK_SENT: case STATE_CLOSING: case STATE_STOPPING: case STATE_OPENED: break; case STATE_ACK_RCVD: sppp_cp_change_state(cp, sp, STATE_REQ_SENT); break; default: printf(SPP_FMT "%s illegal %s in state %s\n", SPP_ARGS(ifp), cp->name, sppp_cp_type_name(h->type), sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } break; case DISC_REQ: if (cp->proto != PPP_LCP) goto illegal; /* Discard the packet. */ break; case ECHO_REQ: if (cp->proto != PPP_LCP) goto illegal; if (sp->state[cp->protoidx] != STATE_OPENED) { if (debug) log(-1, SPP_FMT "lcp echo req but lcp closed\n", SPP_ARGS(ifp)); ++ifp->if_ierrors; break; } if (len < 8) { if (debug) log(-1, SPP_FMT "invalid lcp echo request " "packet length: %d bytes\n", SPP_ARGS(ifp), len); break; } if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) && ntohl (*(long*)(h+1)) == sp->lcp.magic) { /* Line loopback mode detected. */ printf(SPP_FMT "loopback\n", SPP_ARGS(ifp)); sp->pp_loopcnt = MAXALIVECNT * 5; if_down (ifp); sppp_qflush (&sp->pp_cpq); /* Shut down the PPP link. */ /* XXX */ lcp.Down(sp); lcp.Up(sp); break; } *(long*)(h+1) = htonl (sp->lcp.magic); if (debug) log(-1, SPP_FMT "got lcp echo req, sending echo rep\n", SPP_ARGS(ifp)); sppp_cp_send (sp, PPP_LCP, ECHO_REPLY, h->ident, len-4, h+1); break; case ECHO_REPLY: if (cp->proto != PPP_LCP) goto illegal; if (h->ident != sp->lcp.echoid) { ++ifp->if_ierrors; break; } if (len < 8) { if (debug) log(-1, SPP_FMT "lcp invalid echo reply " "packet length: %d bytes\n", SPP_ARGS(ifp), len); break; } if (debug) log(-1, SPP_FMT "lcp got echo rep\n", SPP_ARGS(ifp)); if (!(sp->lcp.opts & (1 << LCP_OPT_MAGIC)) || ntohl (*(long*)(h+1)) != sp->lcp.magic) sp->pp_alivecnt = 0; break; default: /* Unknown packet type -- send Code-Reject packet. */ illegal: if (debug) log(-1, SPP_FMT "%s send code-rej for 0x%x\n", SPP_ARGS(ifp), cp->name, h->type); sppp_cp_send(sp, cp->proto, CODE_REJ, ++sp->pp_seq, m->m_pkthdr.len, h); ++ifp->if_ierrors; } } /* * The generic part of all Up/Down/Open/Close/TO event handlers. * Basically, the state transition handling in the automaton. */ static void sppp_up_event(const struct cp *cp, struct sppp *sp) { STDDCL; if (debug) log(LOG_DEBUG, SPP_FMT "%s up(%s)\n", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx])); switch (sp->state[cp->protoidx]) { case STATE_INITIAL: sppp_cp_change_state(cp, sp, STATE_CLOSED); break; case STATE_STARTING: sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; (cp->scr)(sp); sppp_cp_change_state(cp, sp, STATE_REQ_SENT); break; default: printf(SPP_FMT "%s illegal up in state %s\n", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx])); } } static void sppp_down_event(const struct cp *cp, struct sppp *sp) { STDDCL; if (debug) log(LOG_DEBUG, SPP_FMT "%s down(%s)\n", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx])); switch (sp->state[cp->protoidx]) { case STATE_CLOSED: case STATE_CLOSING: sppp_cp_change_state(cp, sp, STATE_INITIAL); break; case STATE_STOPPED: sppp_cp_change_state(cp, sp, STATE_STARTING); (cp->tls)(sp); break; case STATE_STOPPING: case STATE_REQ_SENT: case STATE_ACK_RCVD: case STATE_ACK_SENT: sppp_cp_change_state(cp, sp, STATE_STARTING); break; case STATE_OPENED: (cp->tld)(sp); sppp_cp_change_state(cp, sp, STATE_STARTING); break; default: printf(SPP_FMT "%s illegal down in state %s\n", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx])); } } static void sppp_open_event(const struct cp *cp, struct sppp *sp) { STDDCL; if (debug) log(LOG_DEBUG, SPP_FMT "%s open(%s)\n", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx])); switch (sp->state[cp->protoidx]) { case STATE_INITIAL: sppp_cp_change_state(cp, sp, STATE_STARTING); (cp->tls)(sp); break; case STATE_STARTING: break; case STATE_CLOSED: sp->rst_counter[cp->protoidx] = sp->lcp.max_configure; (cp->scr)(sp); sppp_cp_change_state(cp, sp, STATE_REQ_SENT); break; case STATE_STOPPED: case STATE_STOPPING: case STATE_REQ_SENT: case STATE_ACK_RCVD: case STATE_ACK_SENT: case STATE_OPENED: break; case STATE_CLOSING: sppp_cp_change_state(cp, sp, STATE_STOPPING); break; } } static void sppp_close_event(const struct cp *cp, struct sppp *sp) { STDDCL; if (debug) log(LOG_DEBUG, SPP_FMT "%s close(%s)\n", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx])); switch (sp->state[cp->protoidx]) { case STATE_INITIAL: case STATE_CLOSED: case STATE_CLOSING: break; case STATE_STARTING: sppp_cp_change_state(cp, sp, STATE_INITIAL); (cp->tlf)(sp); break; case STATE_STOPPED: sppp_cp_change_state(cp, sp, STATE_CLOSED); break; case STATE_STOPPING: sppp_cp_change_state(cp, sp, STATE_CLOSING); break; case STATE_OPENED: (cp->tld)(sp); /* fall through */ case STATE_REQ_SENT: case STATE_ACK_RCVD: case STATE_ACK_SENT: sp->rst_counter[cp->protoidx] = sp->lcp.max_terminate; sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0); sppp_cp_change_state(cp, sp, STATE_CLOSING); break; } } static void sppp_to_event(const struct cp *cp, struct sppp *sp) { STDDCL; int s; s = splimp(); if (debug) log(LOG_DEBUG, SPP_FMT "%s TO(%s) rst_counter = %d\n", SPP_ARGS(ifp), cp->name, sppp_state_name(sp->state[cp->protoidx]), sp->rst_counter[cp->protoidx]); if (--sp->rst_counter[cp->protoidx] < 0) /* TO- event */ switch (sp->state[cp->protoidx]) { case STATE_CLOSING: sppp_cp_change_state(cp, sp, STATE_CLOSED); (cp->tlf)(sp); break; case STATE_STOPPING: sppp_cp_change_state(cp, sp, STATE_STOPPED); (cp->tlf)(sp); break; case STATE_REQ_SENT: case STATE_ACK_RCVD: case STATE_ACK_SENT: sppp_cp_change_state(cp, sp, STATE_STOPPED); (cp->tlf)(sp); break; } else /* TO+ event */ switch (sp->state[cp->protoidx]) { case STATE_CLOSING: case STATE_STOPPING: sppp_cp_send(sp, cp->proto, TERM_REQ, ++sp->pp_seq, 0, 0); TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, sp->ch[cp->protoidx]); break; case STATE_REQ_SENT: case STATE_ACK_RCVD: (cp->scr)(sp); /* sppp_cp_change_state() will restart the timer */ sppp_cp_change_state(cp, sp, STATE_REQ_SENT); break; case STATE_ACK_SENT: (cp->scr)(sp); TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, sp->ch[cp->protoidx]); break; } splx(s); } /* * Change the state of a control protocol in the state automaton. * Takes care of starting/stopping the restart timer. */ void sppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate) { sp->state[cp->protoidx] = newstate; UNTIMEOUT(cp->TO, (void *)sp, sp->ch[cp->protoidx]); switch (newstate) { case STATE_INITIAL: case STATE_STARTING: case STATE_CLOSED: case STATE_STOPPED: case STATE_OPENED: break; case STATE_CLOSING: case STATE_STOPPING: case STATE_REQ_SENT: case STATE_ACK_RCVD: case STATE_ACK_SENT: TIMEOUT(cp->TO, (void *)sp, sp->lcp.timeout, sp->ch[cp->protoidx]); break; } } /* *--------------------------------------------------------------------------* * * * The LCP implementation. * * * *--------------------------------------------------------------------------* */ static void sppp_lcp_init(struct sppp *sp) { sp->lcp.opts = (1 << LCP_OPT_MAGIC); sp->lcp.magic = 0; sp->state[IDX_LCP] = STATE_INITIAL; sp->fail_counter[IDX_LCP] = 0; sp->lcp.protos = 0; sp->lcp.mru = sp->lcp.their_mru = PP_MTU; /* Note that these values are relevant for all control protocols */ sp->lcp.timeout = 3 * hz; sp->lcp.max_terminate = 2; sp->lcp.max_configure = 10; sp->lcp.max_failure = 10; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_LCP]); #endif } static void sppp_lcp_up(struct sppp *sp) { STDDCL; sp->pp_alivecnt = 0; sp->lcp.opts = (1 << LCP_OPT_MAGIC); sp->lcp.magic = 0; sp->lcp.protos = 0; sp->lcp.mru = sp->lcp.their_mru = PP_MTU; /* * If this interface is passive or dial-on-demand, and we are * still in Initial state, it means we've got an incoming * call. Activate the interface. */ if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) != 0) { if (debug) log(LOG_DEBUG, SPP_FMT "Up event", SPP_ARGS(ifp)); ifp->if_flags |= IFF_RUNNING; if (sp->state[IDX_LCP] == STATE_INITIAL) { if (debug) log(-1, "(incoming call)\n"); sp->pp_flags |= PP_CALLIN; lcp.Open(sp); } else if (debug) log(-1, "\n"); } sppp_up_event(&lcp, sp); } static void sppp_lcp_down(struct sppp *sp) { STDDCL; sppp_down_event(&lcp, sp); /* * If this is neither a dial-on-demand nor a passive * interface, simulate an ``ifconfig down'' action, so the * administrator can force a redial by another ``ifconfig * up''. XXX For leased line operation, should we immediately * try to reopen the connection here? */ if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) == 0) { log(LOG_INFO, SPP_FMT "Down event, taking interface down.\n", SPP_ARGS(ifp)); if_down(ifp); } else { if (debug) log(LOG_DEBUG, SPP_FMT "Down event (carrier loss)\n", SPP_ARGS(ifp)); sp->pp_flags &= ~PP_CALLIN; if (sp->state[IDX_LCP] != STATE_INITIAL) lcp.Close(sp); ifp->if_flags &= ~IFF_RUNNING; } } static void sppp_lcp_open(struct sppp *sp) { /* * If we are authenticator, negotiate LCP_AUTH */ if (sp->hisauth.proto != 0) sp->lcp.opts |= (1 << LCP_OPT_AUTH_PROTO); else sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO); sp->pp_flags &= ~PP_NEEDAUTH; sppp_open_event(&lcp, sp); } static void sppp_lcp_close(struct sppp *sp) { sppp_close_event(&lcp, sp); } static void sppp_lcp_TO(void *cookie) { sppp_to_event(&lcp, (struct sppp *)cookie); } /* * Analyze a configure request. Return true if it was agreeable, and * caused action sca, false if it has been rejected or nak'ed, and * caused action scn. (The return value is used to make the state * transition decision in the state automaton.) */ static int sppp_lcp_RCR(struct sppp *sp, struct lcp_header *h, int len) { STDDCL; u_char *buf, *r, *p; int origlen, rlen; u_long nmagic; u_short authproto; len -= 4; origlen = len; buf = r = malloc (len, M_TEMP, M_NOWAIT); if (! buf) return (0); if (debug) log(LOG_DEBUG, SPP_FMT "lcp parse opts: ", SPP_ARGS(ifp)); /* pass 1: check for things that need to be rejected */ p = (void*) (h+1); for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { if (debug) log(-1, " %s ", sppp_lcp_opt_name(*p)); switch (*p) { case LCP_OPT_MAGIC: /* Magic number. */ if (len >= 6 && p[1] == 6) continue; if (debug) log(-1, "[invalid] "); break; case LCP_OPT_ASYNC_MAP: /* Async control character map. */ if (len >= 6 && p[1] == 6) continue; if (debug) log(-1, "[invalid] "); break; case LCP_OPT_MRU: /* Maximum receive unit. */ if (len >= 4 && p[1] == 4) continue; if (debug) log(-1, "[invalid] "); break; case LCP_OPT_AUTH_PROTO: if (len < 4) { if (debug) log(-1, "[invalid] "); break; } authproto = (p[2] << 8) + p[3]; if (authproto == PPP_CHAP && p[1] != 5) { if (debug) log(-1, "[invalid chap len] "); break; } if (sp->myauth.proto == 0) { /* we are not configured to do auth */ if (debug) log(-1, "[not configured] "); break; } /* * Remote want us to authenticate, remember this, * so we stay in PHASE_AUTHENTICATE after LCP got * up. */ sp->pp_flags |= PP_NEEDAUTH; continue; default: /* Others not supported. */ if (debug) log(-1, "[rej] "); break; } /* Add the option to rejected list. */ bcopy (p, r, p[1]); r += p[1]; rlen += p[1]; } if (rlen) { if (debug) log(-1, " send conf-rej\n"); sppp_cp_send (sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf); return 0; } else if (debug) log(-1, "\n"); /* * pass 2: check for option values that are unacceptable and * thus require to be nak'ed. */ if (debug) log(LOG_DEBUG, SPP_FMT "lcp parse opt values: ", SPP_ARGS(ifp)); p = (void*) (h+1); len = origlen; for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { if (debug) log(-1, " %s ", sppp_lcp_opt_name(*p)); switch (*p) { case LCP_OPT_MAGIC: /* Magic number -- extract. */ nmagic = (u_long)p[2] << 24 | (u_long)p[3] << 16 | p[4] << 8 | p[5]; if (nmagic != sp->lcp.magic) { sp->pp_loopcnt = 0; if (debug) log(-1, "0x%lx ", nmagic); continue; } if (debug && sp->pp_loopcnt < MAXALIVECNT*5) log(-1, "[glitch] "); ++sp->pp_loopcnt; /* * We negate our magic here, and NAK it. If * we see it later in an NAK packet, we * suggest a new one. */ nmagic = ~sp->lcp.magic; /* Gonna NAK it. */ p[2] = nmagic >> 24; p[3] = nmagic >> 16; p[4] = nmagic >> 8; p[5] = nmagic; break; case LCP_OPT_ASYNC_MAP: /* Async control character map -- check to be zero. */ if (! p[2] && ! p[3] && ! p[4] && ! p[5]) { if (debug) log(-1, "[empty] "); continue; } if (debug) log(-1, "[non-empty] "); /* suggest a zero one */ p[2] = p[3] = p[4] = p[5] = 0; break; case LCP_OPT_MRU: /* * Maximum receive unit. Always agreeable, * but ignored by now. */ sp->lcp.their_mru = p[2] * 256 + p[3]; if (debug) log(-1, "%lu ", sp->lcp.their_mru); continue; case LCP_OPT_AUTH_PROTO: authproto = (p[2] << 8) + p[3]; if (sp->myauth.proto != authproto) { /* not agreed, nak */ if (debug) log(-1, "[mine %s != his %s] ", sppp_proto_name(sp->hisauth.proto), sppp_proto_name(authproto)); p[2] = sp->myauth.proto >> 8; p[3] = sp->myauth.proto; break; } if (authproto == PPP_CHAP && p[4] != CHAP_MD5) { if (debug) log(-1, "[chap not MD5] "); p[4] = CHAP_MD5; break; } continue; } /* Add the option to nak'ed list. */ bcopy (p, r, p[1]); r += p[1]; rlen += p[1]; } if (rlen) { /* * Local and remote magics equal -- loopback? */ if (sp->pp_loopcnt >= MAXALIVECNT*5) { if (sp->pp_loopcnt == MAXALIVECNT*5) printf (SPP_FMT "loopback\n", SPP_ARGS(ifp)); if (ifp->if_flags & IFF_UP) { if_down(ifp); sppp_qflush(&sp->pp_cpq); /* XXX ? */ lcp.Down(sp); lcp.Up(sp); } } else if (++sp->fail_counter[IDX_LCP] >= sp->lcp.max_failure) { if (debug) log(-1, " max_failure (%d) exceeded, " "send conf-rej\n", sp->lcp.max_failure); sppp_cp_send(sp, PPP_LCP, CONF_REJ, h->ident, rlen, buf); } else { if (debug) log(-1, " send conf-nak\n"); sppp_cp_send (sp, PPP_LCP, CONF_NAK, h->ident, rlen, buf); } } else { if (debug) log(-1, " send conf-ack\n"); sp->fail_counter[IDX_LCP] = 0; sp->pp_loopcnt = 0; sppp_cp_send (sp, PPP_LCP, CONF_ACK, h->ident, origlen, h+1); } free (buf, M_TEMP); return (rlen == 0); } /* * Analyze the LCP Configure-Reject option list, and adjust our * negotiation. */ static void sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) { STDDCL; u_char *buf, *p; len -= 4; buf = malloc (len, M_TEMP, M_NOWAIT); if (!buf) return; if (debug) log(LOG_DEBUG, SPP_FMT "lcp rej opts: ", SPP_ARGS(ifp)); p = (void*) (h+1); for (; len > 1 && p[1]; len -= p[1], p += p[1]) { if (debug) log(-1, " %s ", sppp_lcp_opt_name(*p)); switch (*p) { case LCP_OPT_MAGIC: /* Magic number -- can't use it, use 0 */ sp->lcp.opts &= ~(1 << LCP_OPT_MAGIC); sp->lcp.magic = 0; break; case LCP_OPT_MRU: /* * Should not be rejected anyway, since we only * negotiate a MRU if explicitly requested by * peer. */ sp->lcp.opts &= ~(1 << LCP_OPT_MRU); break; case LCP_OPT_AUTH_PROTO: /* * Peer doesn't want to authenticate himself, * deny unless this is a dialout call, and * AUTHFLAG_NOCALLOUT is set. */ if ((sp->pp_flags & PP_CALLIN) == 0 && (sp->hisauth.flags & AUTHFLAG_NOCALLOUT) != 0) { if (debug) log(-1, "[don't insist on auth " "for callout]"); sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO); break; } if (debug) log(-1, "[access denied]\n"); lcp.Close(sp); break; } } if (debug) log(-1, "\n"); free (buf, M_TEMP); return; } /* * Analyze the LCP Configure-NAK option list, and adjust our * negotiation. */ static void sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) { STDDCL; u_char *buf, *p; u_long magic; len -= 4; buf = malloc (len, M_TEMP, M_NOWAIT); if (!buf) return; if (debug) log(LOG_DEBUG, SPP_FMT "lcp nak opts: ", SPP_ARGS(ifp)); p = (void*) (h+1); for (; len > 1 && p[1]; len -= p[1], p += p[1]) { if (debug) log(-1, " %s ", sppp_lcp_opt_name(*p)); switch (*p) { case LCP_OPT_MAGIC: /* Magic number -- renegotiate */ if ((sp->lcp.opts & (1 << LCP_OPT_MAGIC)) && len >= 6 && p[1] == 6) { magic = (u_long)p[2] << 24 | (u_long)p[3] << 16 | p[4] << 8 | p[5]; /* * If the remote magic is our negated one, * this looks like a loopback problem. * Suggest a new magic to make sure. */ if (magic == ~sp->lcp.magic) { if (debug) log(-1, "magic glitch "); #if defined(__FreeBSD__) && __FreeBSD__ >= 3 sp->lcp.magic = random(); #else sp->lcp.magic = time.tv_sec + time.tv_usec; #endif } else { sp->lcp.magic = magic; if (debug) log(-1, "%lu ", magic); } } break; case LCP_OPT_MRU: /* * Peer wants to advise us to negotiate an MRU. * Agree on it if it's reasonable, or use * default otherwise. */ if (len >= 4 && p[1] == 4) { u_int mru = p[2] * 256 + p[3]; if (debug) log(-1, "%d ", mru); if (mru < PP_MTU || mru > PP_MAX_MRU) mru = PP_MTU; sp->lcp.mru = mru; sp->lcp.opts |= (1 << LCP_OPT_MRU); } break; case LCP_OPT_AUTH_PROTO: /* * Peer doesn't like our authentication method, * deny. */ if (debug) log(-1, "[access denied]\n"); lcp.Close(sp); break; } } if (debug) log(-1, "\n"); free (buf, M_TEMP); return; } static void sppp_lcp_tlu(struct sppp *sp) { STDDCL; int i; u_long mask; /* XXX ? */ if (! (ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING)) { /* Coming out of loopback mode. */ if_up(ifp); printf (SPP_FMT "up\n", SPP_ARGS(ifp)); } for (i = 0; i < IDX_COUNT; i++) if ((cps[i])->flags & CP_QUAL) (cps[i])->Open(sp); if ((sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0 || (sp->pp_flags & PP_NEEDAUTH) != 0) sp->pp_phase = PHASE_AUTHENTICATE; else sp->pp_phase = PHASE_NETWORK; if (debug) log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp), sppp_phase_name(sp->pp_phase)); /* * Open all authentication protocols. This is even required * if we already proceeded to network phase, since it might be * that remote wants us to authenticate, so we might have to * send a PAP request. Undesired authentication protocols * don't do anything when they get an Open event. */ for (i = 0; i < IDX_COUNT; i++) if ((cps[i])->flags & CP_AUTH) (cps[i])->Open(sp); if (sp->pp_phase == PHASE_NETWORK) { /* Notify all NCPs. */ for (i = 0; i < IDX_COUNT; i++) if ((cps[i])->flags & CP_NCP) (cps[i])->Open(sp); } /* Send Up events to all started protos. */ for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) (cps[i])->Up(sp); /* notify low-level driver of state change */ if (sp->pp_chg) sp->pp_chg(sp, (int)sp->pp_phase); if (sp->pp_phase == PHASE_NETWORK) /* if no NCP is starting, close down */ sppp_lcp_check_and_close(sp); } static void sppp_lcp_tld(struct sppp *sp) { STDDCL; int i; u_long mask; sp->pp_phase = PHASE_TERMINATE; if (debug) log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp), sppp_phase_name(sp->pp_phase)); /* * Take upper layers down. We send the Down event first and * the Close second to prevent the upper layers from sending * ``a flurry of terminate-request packets'', as the RFC * describes it. */ for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) if (sp->lcp.protos & mask && ((cps[i])->flags & CP_LCP) == 0) { (cps[i])->Down(sp); (cps[i])->Close(sp); } } static void sppp_lcp_tls(struct sppp *sp) { STDDCL; sp->pp_phase = PHASE_ESTABLISH; if (debug) log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp), sppp_phase_name(sp->pp_phase)); /* Notify lower layer if desired. */ if (sp->pp_tls) (sp->pp_tls)(sp); else (sp->pp_up)(sp); } static void sppp_lcp_tlf(struct sppp *sp) { STDDCL; sp->pp_phase = PHASE_DEAD; if (debug) log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp), sppp_phase_name(sp->pp_phase)); /* Notify lower layer if desired. */ if (sp->pp_tlf) (sp->pp_tlf)(sp); else (sp->pp_down)(sp); } static void sppp_lcp_scr(struct sppp *sp) { char opt[6 /* magicnum */ + 4 /* mru */ + 5 /* chap */]; int i = 0; u_short authproto; if (sp->lcp.opts & (1 << LCP_OPT_MAGIC)) { if (! sp->lcp.magic) #if defined(__FreeBSD__) && __FreeBSD__ >= 3 sp->lcp.magic = random(); #else sp->lcp.magic = time.tv_sec + time.tv_usec; #endif opt[i++] = LCP_OPT_MAGIC; opt[i++] = 6; opt[i++] = sp->lcp.magic >> 24; opt[i++] = sp->lcp.magic >> 16; opt[i++] = sp->lcp.magic >> 8; opt[i++] = sp->lcp.magic; } if (sp->lcp.opts & (1 << LCP_OPT_MRU)) { opt[i++] = LCP_OPT_MRU; opt[i++] = 4; opt[i++] = sp->lcp.mru >> 8; opt[i++] = sp->lcp.mru; } if (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) { authproto = sp->hisauth.proto; opt[i++] = LCP_OPT_AUTH_PROTO; opt[i++] = authproto == PPP_CHAP? 5: 4; opt[i++] = authproto >> 8; opt[i++] = authproto; if (authproto == PPP_CHAP) opt[i++] = CHAP_MD5; } sp->confid[IDX_LCP] = ++sp->pp_seq; sppp_cp_send (sp, PPP_LCP, CONF_REQ, sp->confid[IDX_LCP], i, &opt); } /* * Check the open NCPs, return true if at least one NCP is open. */ static int sppp_ncp_check(struct sppp *sp) { int i, mask; for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) if (sp->lcp.protos & mask && (cps[i])->flags & CP_NCP) return 1; return 0; } /* * Re-check the open NCPs and see if we should terminate the link. * Called by the NCPs during their tlf action handling. */ static void sppp_lcp_check_and_close(struct sppp *sp) { if (sp->pp_phase < PHASE_NETWORK) /* don't bother, we are already going down */ return; if (sppp_ncp_check(sp)) return; lcp.Close(sp); } /* *--------------------------------------------------------------------------* * * * The IPCP implementation. * * * *--------------------------------------------------------------------------* */ static void sppp_ipcp_init(struct sppp *sp) { sp->ipcp.opts = 0; sp->ipcp.flags = 0; sp->state[IDX_IPCP] = STATE_INITIAL; sp->fail_counter[IDX_IPCP] = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_IPCP]); #endif } static void sppp_ipcp_up(struct sppp *sp) { sppp_up_event(&ipcp, sp); } static void sppp_ipcp_down(struct sppp *sp) { sppp_down_event(&ipcp, sp); } static void sppp_ipcp_open(struct sppp *sp) { STDDCL; u_long myaddr, hisaddr; sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN|IPCP_MYADDR_SEEN|IPCP_MYADDR_DYN); sppp_get_ip_addrs(sp, &myaddr, &hisaddr, 0); /* * If we don't have his address, this probably means our * interface doesn't want to talk IP at all. (This could * be the case if somebody wants to speak only IPX, for * example.) Don't open IPCP in this case. */ if (hisaddr == 0L) { /* XXX this message should go away */ if (debug) log(LOG_DEBUG, SPP_FMT "ipcp_open(): no IP interface\n", SPP_ARGS(ifp)); return; } if (myaddr == 0L) { /* * I don't have an assigned address, so i need to * negotiate my address. */ sp->ipcp.flags |= IPCP_MYADDR_DYN; sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS); } else sp->ipcp.flags |= IPCP_MYADDR_SEEN; sppp_open_event(&ipcp, sp); } static void sppp_ipcp_close(struct sppp *sp) { sppp_close_event(&ipcp, sp); if (sp->ipcp.flags & IPCP_MYADDR_DYN) /* * My address was dynamic, clear it again. */ sppp_set_ip_addr(sp, 0L); } static void sppp_ipcp_TO(void *cookie) { sppp_to_event(&ipcp, (struct sppp *)cookie); } /* * Analyze a configure request. Return true if it was agreeable, and * caused action sca, false if it has been rejected or nak'ed, and * caused action scn. (The return value is used to make the state * transition decision in the state automaton.) */ static int sppp_ipcp_RCR(struct sppp *sp, struct lcp_header *h, int len) { u_char *buf, *r, *p; struct ifnet *ifp = &sp->pp_if; int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG; u_long hisaddr, desiredaddr; int gotmyaddr = 0; len -= 4; origlen = len; /* * Make sure to allocate a buf that can at least hold a * conf-nak with an `address' option. We might need it below. */ buf = r = malloc ((len < 6? 6: len), M_TEMP, M_NOWAIT); if (! buf) return (0); /* pass 1: see if we can recognize them */ if (debug) log(LOG_DEBUG, SPP_FMT "ipcp parse opts: ", SPP_ARGS(ifp)); p = (void*) (h+1); for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { case IPCP_OPT_ADDRESS: if (len >= 6 && p[1] == 6) { /* correctly formed address option */ continue; } if (debug) log(-1, "[invalid] "); break; default: /* Others not supported. */ if (debug) log(-1, "[rej] "); break; } /* Add the option to rejected list. */ bcopy (p, r, p[1]); r += p[1]; rlen += p[1]; } if (rlen) { if (debug) log(-1, " send conf-rej\n"); sppp_cp_send (sp, PPP_IPCP, CONF_REJ, h->ident, rlen, buf); return 0; } else if (debug) log(-1, "\n"); /* pass 2: parse option values */ sppp_get_ip_addrs(sp, 0, &hisaddr, 0); if (debug) log(LOG_DEBUG, SPP_FMT "ipcp parse opt values: ", SPP_ARGS(ifp)); p = (void*) (h+1); len = origlen; for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { case IPCP_OPT_ADDRESS: /* This is the address he wants in his end */ desiredaddr = p[2] << 24 | p[3] << 16 | p[4] << 8 | p[5]; if (desiredaddr == hisaddr || (hisaddr == 1 && desiredaddr != 0)) { /* * Peer's address is same as our value, * or we have set it to 0.0.0.1 to * indicate that we do not really care, * this is agreeable. Gonna conf-ack * it. */ if (debug) log(-1, "%s [ack] ", sppp_dotted_quad(hisaddr)); /* record that we've seen it already */ sp->ipcp.flags |= IPCP_HISADDR_SEEN; continue; } /* * The address wasn't agreeable. This is either * he sent us 0.0.0.0, asking to assign him an * address, or he send us another address not * matching our value. Either case, we gonna * conf-nak it with our value. * XXX: we should "rej" if hisaddr == 0 */ if (debug) { if (desiredaddr == 0) log(-1, "[addr requested] "); else log(-1, "%s [not agreed] ", sppp_dotted_quad(desiredaddr)); } p[2] = hisaddr >> 24; p[3] = hisaddr >> 16; p[4] = hisaddr >> 8; p[5] = hisaddr; break; } /* Add the option to nak'ed list. */ bcopy (p, r, p[1]); r += p[1]; rlen += p[1]; } /* * If we are about to conf-ack the request, but haven't seen * his address so far, gonna conf-nak it instead, with the * `address' option present and our idea of his address being * filled in there, to request negotiation of both addresses. * * XXX This can result in an endless req - nak loop if peer * doesn't want to send us his address. Q: What should we do * about it? XXX A: implement the max-failure counter. */ if (rlen == 0 && !(sp->ipcp.flags & IPCP_HISADDR_SEEN) && !gotmyaddr) { buf[0] = IPCP_OPT_ADDRESS; buf[1] = 6; buf[2] = hisaddr >> 24; buf[3] = hisaddr >> 16; buf[4] = hisaddr >> 8; buf[5] = hisaddr; rlen = 6; if (debug) log(-1, "still need hisaddr "); } if (rlen) { if (debug) log(-1, " send conf-nak\n"); sppp_cp_send (sp, PPP_IPCP, CONF_NAK, h->ident, rlen, buf); } else { if (debug) log(-1, " send conf-ack\n"); sppp_cp_send (sp, PPP_IPCP, CONF_ACK, h->ident, origlen, h+1); } free (buf, M_TEMP); return (rlen == 0); } /* * Analyze the IPCP Configure-Reject option list, and adjust our * negotiation. */ static void sppp_ipcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) { u_char *buf, *p; struct ifnet *ifp = &sp->pp_if; int debug = ifp->if_flags & IFF_DEBUG; len -= 4; buf = malloc (len, M_TEMP, M_NOWAIT); if (!buf) return; if (debug) log(LOG_DEBUG, SPP_FMT "ipcp rej opts: ", SPP_ARGS(ifp)); p = (void*) (h+1); for (; len > 1 && p[1]; len -= p[1], p += p[1]) { if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { case IPCP_OPT_ADDRESS: /* * Peer doesn't grok address option. This is * bad. XXX Should we better give up here? * XXX We could try old "addresses" option... */ sp->ipcp.opts &= ~(1 << IPCP_OPT_ADDRESS); break; } } if (debug) log(-1, "\n"); free (buf, M_TEMP); return; } /* * Analyze the IPCP Configure-NAK option list, and adjust our * negotiation. */ static void sppp_ipcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) { u_char *buf, *p; struct ifnet *ifp = &sp->pp_if; int debug = ifp->if_flags & IFF_DEBUG; u_long wantaddr; len -= 4; buf = malloc (len, M_TEMP, M_NOWAIT); if (!buf) return; if (debug) log(LOG_DEBUG, SPP_FMT "ipcp nak opts: ", SPP_ARGS(ifp)); p = (void*) (h+1); for (; len > 1 && p[1]; len -= p[1], p += p[1]) { if (debug) log(-1, " %s ", sppp_ipcp_opt_name(*p)); switch (*p) { case IPCP_OPT_ADDRESS: /* * Peer doesn't like our local IP address. See * if we can do something for him. We'll drop * him our address then. */ if (len >= 6 && p[1] == 6) { wantaddr = p[2] << 24 | p[3] << 16 | p[4] << 8 | p[5]; sp->ipcp.opts |= (1 << IPCP_OPT_ADDRESS); if (debug) log(-1, "[wantaddr %s] ", sppp_dotted_quad(wantaddr)); /* * When doing dynamic address assignment, * we accept his offer. Otherwise, we * ignore it and thus continue to negotiate * our already existing value. * XXX: Bogus, if he said no once, he'll * just say no again, might as well die. */ if (sp->ipcp.flags & IPCP_MYADDR_DYN) { sppp_set_ip_addr(sp, wantaddr); if (debug) log(-1, "[agree] "); sp->ipcp.flags |= IPCP_MYADDR_SEEN; } } break; } } if (debug) log(-1, "\n"); free (buf, M_TEMP); return; } static void sppp_ipcp_tlu(struct sppp *sp) { /* we are up - notify isdn daemon */ if (sp->pp_con) sp->pp_con(sp); } static void sppp_ipcp_tld(struct sppp *sp) { } static void sppp_ipcp_tls(struct sppp *sp) { /* indicate to LCP that it must stay alive */ sp->lcp.protos |= (1 << IDX_IPCP); } static void sppp_ipcp_tlf(struct sppp *sp) { /* we no longer need LCP */ sp->lcp.protos &= ~(1 << IDX_IPCP); sppp_lcp_check_and_close(sp); } static void sppp_ipcp_scr(struct sppp *sp) { char opt[6 /* compression */ + 6 /* address */]; u_long ouraddr; int i = 0; if (sp->ipcp.opts & (1 << IPCP_OPT_ADDRESS)) { sppp_get_ip_addrs(sp, &ouraddr, 0, 0); opt[i++] = IPCP_OPT_ADDRESS; opt[i++] = 6; opt[i++] = ouraddr >> 24; opt[i++] = ouraddr >> 16; opt[i++] = ouraddr >> 8; opt[i++] = ouraddr; } sp->confid[IDX_IPCP] = ++sp->pp_seq; sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt); } /* *--------------------------------------------------------------------------* * * * The CHAP implementation. * * * *--------------------------------------------------------------------------* */ /* * The authentication protocols don't employ a full-fledged state machine as * the control protocols do, since they do have Open and Close events, but * not Up and Down, nor are they explicitly terminated. Also, use of the * authentication protocols may be different in both directions (this makes * sense, think of a machine that never accepts incoming calls but only * calls out, it doesn't require the called party to authenticate itself). * * Our state machine for the local authentication protocol (we are requesting * the peer to authenticate) looks like: * * RCA- * +--------------------------------------------+ * V scn,tld| * +--------+ Close +---------+ RCA+ * | |<----------------------------------| |------+ * +--->| Closed | TO* | Opened | sca | * | | |-----+ +-------| |<-----+ * | +--------+ irc | | +---------+ * | ^ | | ^ * | | | | | * | | | | | * | TO-| | | | * | |tld TO+ V | | * | | +------->+ | | * | | | | | | * | +--------+ V | | * | | |<----+<--------------------+ | * | | Req- | scr | * | | Sent | | * | | | | * | +--------+ | * | RCA- | | RCA+ | * +------+ +------------------------------------------+ * scn,tld sca,irc,ict,tlu * * * with: * * Open: LCP reached authentication phase * Close: LCP reached terminate phase * * RCA+: received reply (pap-req, chap-response), acceptable * RCN: received reply (pap-req, chap-response), not acceptable * TO+: timeout with restart counter >= 0 * TO-: timeout with restart counter < 0 * TO*: reschedule timeout for CHAP * * scr: send request packet (none for PAP, chap-challenge) * sca: send ack packet (pap-ack, chap-success) * scn: send nak packet (pap-nak, chap-failure) * ict: initialize re-challenge timer (CHAP only) * * tlu: this-layer-up, LCP reaches network phase * tld: this-layer-down, LCP enters terminate phase * * Note that in CHAP mode, after sending a new challenge, while the state * automaton falls back into Req-Sent state, it doesn't signal a tld * event to LCP, so LCP remains in network phase. Only after not getting * any response (or after getting an unacceptable response), CHAP closes, * causing LCP to enter terminate phase. * * With PAP, there is no initial request that can be sent. The peer is * expected to send one based on the successful negotiation of PAP as * the authentication protocol during the LCP option negotiation. * * Incoming authentication protocol requests (remote requests * authentication, we are peer) don't employ a state machine at all, * they are simply answered. Some peers [Ascend P50 firmware rev * 4.50] react allergically when sending IPCP requests while they are * still in authentication phase (thereby violating the standard that * demands that these NCP packets are to be discarded), so we keep * track of the peer demanding us to authenticate, and only proceed to * phase network once we've seen a positive acknowledge for the * authentication. */ /* * Handle incoming CHAP packets. */ void sppp_chap_input(struct sppp *sp, struct mbuf *m) { STDDCL; struct lcp_header *h; int len, x; u_char *value, *name, digest[AUTHKEYLEN], dsize; int value_len, name_len; MD5_CTX ctx; len = m->m_pkthdr.len; if (len < 4) { if (debug) log(LOG_DEBUG, SPP_FMT "chap invalid packet length: %d bytes\n", SPP_ARGS(ifp), len); return; } h = mtod (m, struct lcp_header*); if (len > ntohs (h->len)) len = ntohs (h->len); switch (h->type) { /* challenge, failure and success are his authproto */ case CHAP_CHALLENGE: value = 1 + (u_char*)(h+1); value_len = value[-1]; name = value + value_len; name_len = len - value_len - 5; if (name_len < 0) { if (debug) { log(LOG_DEBUG, SPP_FMT "chap corrupted challenge " "<%s id=0x%x len=%d", SPP_ARGS(ifp), sppp_auth_type_name(PPP_CHAP, h->type), h->ident, ntohs(h->len)); sppp_print_bytes((u_char*) (h+1), len-4); log(-1, ">\n"); } break; } if (debug) { log(LOG_DEBUG, SPP_FMT "chap input <%s id=0x%x len=%d name=", SPP_ARGS(ifp), sppp_auth_type_name(PPP_CHAP, h->type), h->ident, ntohs(h->len)); sppp_print_string((char*) name, name_len); log(-1, " value-size=%d value=", value_len); sppp_print_bytes(value, value_len); log(-1, ">\n"); } /* Compute reply value. */ MD5Init(&ctx); MD5Update(&ctx, &h->ident, 1); MD5Update(&ctx, sp->myauth.secret, sppp_strnlen(sp->myauth.secret, AUTHKEYLEN)); MD5Update(&ctx, value, value_len); MD5Final(digest, &ctx); dsize = sizeof digest; sppp_auth_send(&chap, sp, CHAP_RESPONSE, h->ident, sizeof dsize, (const char *)&dsize, sizeof digest, digest, (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN), sp->myauth.name, 0); break; case CHAP_SUCCESS: if (debug) { log(LOG_DEBUG, SPP_FMT "chap success", SPP_ARGS(ifp)); if (len > 4) { log(-1, ": "); sppp_print_string((char*)(h + 1), len - 4); } log(-1, "\n"); } x = splimp(); sp->pp_flags &= ~PP_NEEDAUTH; if (sp->myauth.proto == PPP_CHAP && (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) && (sp->lcp.protos & (1 << IDX_CHAP)) == 0) { /* * We are authenticator for CHAP but didn't * complete yet. Leave it to tlu to proceed * to network phase. */ splx(x); break; } splx(x); sppp_phase_network(sp); break; case CHAP_FAILURE: if (debug) { log(LOG_INFO, SPP_FMT "chap failure", SPP_ARGS(ifp)); if (len > 4) { log(-1, ": "); sppp_print_string((char*)(h + 1), len - 4); } log(-1, "\n"); } else log(LOG_INFO, SPP_FMT "chap failure\n", SPP_ARGS(ifp)); /* await LCP shutdown by authenticator */ break; /* response is my authproto */ case CHAP_RESPONSE: value = 1 + (u_char*)(h+1); value_len = value[-1]; name = value + value_len; name_len = len - value_len - 5; if (name_len < 0) { if (debug) { log(LOG_DEBUG, SPP_FMT "chap corrupted response " "<%s id=0x%x len=%d", SPP_ARGS(ifp), sppp_auth_type_name(PPP_CHAP, h->type), h->ident, ntohs(h->len)); sppp_print_bytes((u_char*)(h+1), len-4); log(-1, ">\n"); } break; } if (h->ident != sp->confid[IDX_CHAP]) { if (debug) log(LOG_DEBUG, SPP_FMT "chap dropping response for old ID " "(got %d, expected %d)\n", SPP_ARGS(ifp), h->ident, sp->confid[IDX_CHAP]); break; } if (name_len != sppp_strnlen(sp->hisauth.name, AUTHNAMELEN) || bcmp(name, sp->hisauth.name, name_len) != 0) { log(LOG_INFO, SPP_FMT "chap response, his name ", SPP_ARGS(ifp)); sppp_print_string(name, name_len); log(-1, " != expected "); sppp_print_string(sp->hisauth.name, sppp_strnlen(sp->hisauth.name, AUTHNAMELEN)); log(-1, "\n"); } if (debug) { log(LOG_DEBUG, SPP_FMT "chap input(%s) " "<%s id=0x%x len=%d name=", SPP_ARGS(ifp), sppp_state_name(sp->state[IDX_CHAP]), sppp_auth_type_name(PPP_CHAP, h->type), h->ident, ntohs (h->len)); sppp_print_string((char*)name, name_len); log(-1, " value-size=%d value=", value_len); sppp_print_bytes(value, value_len); log(-1, ">\n"); } if (value_len != AUTHKEYLEN) { if (debug) log(LOG_DEBUG, SPP_FMT "chap bad hash value length: " "%d bytes, should be %d\n", SPP_ARGS(ifp), value_len, AUTHKEYLEN); break; } MD5Init(&ctx); MD5Update(&ctx, &h->ident, 1); MD5Update(&ctx, sp->hisauth.secret, sppp_strnlen(sp->hisauth.secret, AUTHKEYLEN)); MD5Update(&ctx, sp->myauth.challenge, AUTHKEYLEN); MD5Final(digest, &ctx); #define FAILMSG "Failed..." #define SUCCMSG "Welcome!" if (value_len != sizeof digest || bcmp(digest, value, value_len) != 0) { /* action scn, tld */ sppp_auth_send(&chap, sp, CHAP_FAILURE, h->ident, sizeof(FAILMSG) - 1, (u_char *)FAILMSG, 0); chap.tld(sp); break; } /* action sca, perhaps tlu */ if (sp->state[IDX_CHAP] == STATE_REQ_SENT || sp->state[IDX_CHAP] == STATE_OPENED) sppp_auth_send(&chap, sp, CHAP_SUCCESS, h->ident, sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG, 0); if (sp->state[IDX_CHAP] == STATE_REQ_SENT) { sppp_cp_change_state(&chap, sp, STATE_OPENED); chap.tlu(sp); } break; default: /* Unknown CHAP packet type -- ignore. */ if (debug) { log(LOG_DEBUG, SPP_FMT "chap unknown input(%s) " "<0x%x id=0x%xh len=%d", SPP_ARGS(ifp), sppp_state_name(sp->state[IDX_CHAP]), h->type, h->ident, ntohs(h->len)); sppp_print_bytes((u_char*)(h+1), len-4); log(-1, ">\n"); } break; } } static void sppp_chap_init(struct sppp *sp) { /* Chap doesn't have STATE_INITIAL at all. */ sp->state[IDX_CHAP] = STATE_CLOSED; sp->fail_counter[IDX_CHAP] = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_CHAP]); #endif } static void sppp_chap_open(struct sppp *sp) { if (sp->myauth.proto == PPP_CHAP && (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) { /* we are authenticator for CHAP, start it */ chap.scr(sp); sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure; sppp_cp_change_state(&chap, sp, STATE_REQ_SENT); } /* nothing to be done if we are peer, await a challenge */ } static void sppp_chap_close(struct sppp *sp) { if (sp->state[IDX_CHAP] != STATE_CLOSED) sppp_cp_change_state(&chap, sp, STATE_CLOSED); } static void sppp_chap_TO(void *cookie) { struct sppp *sp = (struct sppp *)cookie; STDDCL; int s; s = splimp(); if (debug) log(LOG_DEBUG, SPP_FMT "chap TO(%s) rst_counter = %d\n", SPP_ARGS(ifp), sppp_state_name(sp->state[IDX_CHAP]), sp->rst_counter[IDX_CHAP]); if (--sp->rst_counter[IDX_CHAP] < 0) /* TO- event */ switch (sp->state[IDX_CHAP]) { case STATE_REQ_SENT: chap.tld(sp); sppp_cp_change_state(&chap, sp, STATE_CLOSED); break; } else /* TO+ (or TO*) event */ switch (sp->state[IDX_CHAP]) { case STATE_OPENED: /* TO* event */ sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure; /* fall through */ case STATE_REQ_SENT: chap.scr(sp); /* sppp_cp_change_state() will restart the timer */ sppp_cp_change_state(&chap, sp, STATE_REQ_SENT); break; } splx(s); } static void sppp_chap_tlu(struct sppp *sp) { STDDCL; int i, x; i = 0; sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure; /* * Some broken CHAP implementations (Conware CoNet, firmware * 4.0.?) don't want to re-authenticate their CHAP once the * initial challenge-response exchange has taken place. * Provide for an option to avoid rechallenges. */ if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0) { /* * Compute the re-challenge timeout. This will yield * a number between 300 and 810 seconds. */ i = 300 + ((unsigned)(random() & 0xff00) >> 7); TIMEOUT(chap.TO, (void *)sp, i * hz, sp->ch[IDX_CHAP]); } if (debug) { log(LOG_DEBUG, SPP_FMT "chap %s, ", SPP_ARGS(ifp), sp->pp_phase == PHASE_NETWORK? "reconfirmed": "tlu"); if ((sp->hisauth.flags & AUTHFLAG_NORECHALLENGE) == 0) log(-1, "next re-challenge in %d seconds\n", i); else log(-1, "re-challenging supressed\n"); } x = splimp(); /* indicate to LCP that we need to be closed down */ sp->lcp.protos |= (1 << IDX_CHAP); if (sp->pp_flags & PP_NEEDAUTH) { /* * Remote is authenticator, but his auth proto didn't * complete yet. Defer the transition to network * phase. */ splx(x); return; } splx(x); /* * If we are already in phase network, we are done here. This * is the case if this is a dummy tlu event after a re-challenge. */ if (sp->pp_phase != PHASE_NETWORK) sppp_phase_network(sp); } static void sppp_chap_tld(struct sppp *sp) { STDDCL; if (debug) log(LOG_DEBUG, SPP_FMT "chap tld\n", SPP_ARGS(ifp)); UNTIMEOUT(chap.TO, (void *)sp, sp->ch[IDX_CHAP]); sp->lcp.protos &= ~(1 << IDX_CHAP); lcp.Close(sp); } static void sppp_chap_scr(struct sppp *sp) { u_long *ch, seed; u_char clen; /* Compute random challenge. */ ch = (u_long *)sp->myauth.challenge; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 read_random(&seed, sizeof seed); #else { struct timeval tv; microtime(&tv); seed = tv.tv_sec ^ tv.tv_usec; } #endif ch[0] = seed ^ random(); ch[1] = seed ^ random(); ch[2] = seed ^ random(); ch[3] = seed ^ random(); clen = AUTHKEYLEN; sp->confid[IDX_CHAP] = ++sp->pp_seq; sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->confid[IDX_CHAP], sizeof clen, (const char *)&clen, (size_t)AUTHKEYLEN, sp->myauth.challenge, (size_t)sppp_strnlen(sp->myauth.name, AUTHNAMELEN), sp->myauth.name, 0); } /* *--------------------------------------------------------------------------* * * * The PAP implementation. * * * *--------------------------------------------------------------------------* */ /* * For PAP, we need to keep a little state also if we are the peer, not the * authenticator. This is since we don't get a request to authenticate, but * have to repeatedly authenticate ourself until we got a response (or the * retry counter is expired). */ /* * Handle incoming PAP packets. */ static void sppp_pap_input(struct sppp *sp, struct mbuf *m) { STDDCL; struct lcp_header *h; int len, x; u_char *name, *passwd, mlen; int name_len, passwd_len; len = m->m_pkthdr.len; if (len < 5) { if (debug) log(LOG_DEBUG, SPP_FMT "pap invalid packet length: %d bytes\n", SPP_ARGS(ifp), len); return; } h = mtod (m, struct lcp_header*); if (len > ntohs (h->len)) len = ntohs (h->len); switch (h->type) { /* PAP request is my authproto */ case PAP_REQ: name = 1 + (u_char*)(h+1); name_len = name[-1]; passwd = name + name_len + 1; if (name_len > len - 6 || (passwd_len = passwd[-1]) > len - 6 - name_len) { if (debug) { log(LOG_DEBUG, SPP_FMT "pap corrupted input " "<%s id=0x%x len=%d", SPP_ARGS(ifp), sppp_auth_type_name(PPP_PAP, h->type), h->ident, ntohs(h->len)); sppp_print_bytes((u_char*)(h+1), len-4); log(-1, ">\n"); } break; } if (debug) { log(LOG_DEBUG, SPP_FMT "pap input(%s) " "<%s id=0x%x len=%d name=", SPP_ARGS(ifp), sppp_state_name(sp->state[IDX_PAP]), sppp_auth_type_name(PPP_PAP, h->type), h->ident, ntohs(h->len)); sppp_print_string((char*)name, name_len); log(-1, " passwd="); sppp_print_string((char*)passwd, passwd_len); log(-1, ">\n"); } if (name_len > AUTHNAMELEN || passwd_len > AUTHKEYLEN || bcmp(name, sp->hisauth.name, name_len) != 0 || bcmp(passwd, sp->hisauth.secret, passwd_len) != 0) { /* action scn, tld */ mlen = sizeof(FAILMSG) - 1; sppp_auth_send(&pap, sp, PAP_NAK, h->ident, sizeof mlen, (const char *)&mlen, sizeof(FAILMSG) - 1, (u_char *)FAILMSG, 0); pap.tld(sp); break; } /* action sca, perhaps tlu */ if (sp->state[IDX_PAP] == STATE_REQ_SENT || sp->state[IDX_PAP] == STATE_OPENED) { mlen = sizeof(SUCCMSG) - 1; sppp_auth_send(&pap, sp, PAP_ACK, h->ident, sizeof mlen, (const char *)&mlen, sizeof(SUCCMSG) - 1, (u_char *)SUCCMSG, 0); } if (sp->state[IDX_PAP] == STATE_REQ_SENT) { sppp_cp_change_state(&pap, sp, STATE_OPENED); pap.tlu(sp); } break; /* ack and nak are his authproto */ case PAP_ACK: UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); if (debug) { log(LOG_DEBUG, SPP_FMT "pap success", SPP_ARGS(ifp)); name_len = *((char *)h); if (len > 5 && name_len) { log(-1, ": "); sppp_print_string((char*)(h+1), name_len); } log(-1, "\n"); } x = splimp(); sp->pp_flags &= ~PP_NEEDAUTH; if (sp->myauth.proto == PPP_PAP && (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) && (sp->lcp.protos & (1 << IDX_PAP)) == 0) { /* * We are authenticator for PAP but didn't * complete yet. Leave it to tlu to proceed * to network phase. */ splx(x); break; } splx(x); sppp_phase_network(sp); break; case PAP_NAK: UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); if (debug) { log(LOG_INFO, SPP_FMT "pap failure", SPP_ARGS(ifp)); name_len = *((char *)h); if (len > 5 && name_len) { log(-1, ": "); sppp_print_string((char*)(h+1), name_len); } log(-1, "\n"); } else log(LOG_INFO, SPP_FMT "pap failure\n", SPP_ARGS(ifp)); /* await LCP shutdown by authenticator */ break; default: /* Unknown PAP packet type -- ignore. */ if (debug) { log(LOG_DEBUG, SPP_FMT "pap corrupted input " "<0x%x id=0x%x len=%d", SPP_ARGS(ifp), h->type, h->ident, ntohs(h->len)); sppp_print_bytes((u_char*)(h+1), len-4); log(-1, ">\n"); } break; } } static void sppp_pap_init(struct sppp *sp) { /* PAP doesn't have STATE_INITIAL at all. */ sp->state[IDX_PAP] = STATE_CLOSED; sp->fail_counter[IDX_PAP] = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_PAP]); callout_handle_init(&sp->pap_my_to_ch); #endif } static void sppp_pap_open(struct sppp *sp) { if (sp->hisauth.proto == PPP_PAP && (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) { /* we are authenticator for PAP, start our timer */ sp->rst_counter[IDX_PAP] = sp->lcp.max_configure; sppp_cp_change_state(&pap, sp, STATE_REQ_SENT); } if (sp->myauth.proto == PPP_PAP) { /* we are peer, send a request, and start a timer */ pap.scr(sp); TIMEOUT(sppp_pap_my_TO, (void *)sp, sp->lcp.timeout, sp->pap_my_to_ch); } } static void sppp_pap_close(struct sppp *sp) { if (sp->state[IDX_PAP] != STATE_CLOSED) sppp_cp_change_state(&pap, sp, STATE_CLOSED); } /* * That's the timeout routine if we are authenticator. Since the * authenticator is basically passive in PAP, we can't do much here. */ static void sppp_pap_TO(void *cookie) { struct sppp *sp = (struct sppp *)cookie; STDDCL; int s; s = splimp(); if (debug) log(LOG_DEBUG, SPP_FMT "pap TO(%s) rst_counter = %d\n", SPP_ARGS(ifp), sppp_state_name(sp->state[IDX_PAP]), sp->rst_counter[IDX_PAP]); if (--sp->rst_counter[IDX_PAP] < 0) /* TO- event */ switch (sp->state[IDX_PAP]) { case STATE_REQ_SENT: pap.tld(sp); sppp_cp_change_state(&pap, sp, STATE_CLOSED); break; } else /* TO+ event, not very much we could do */ switch (sp->state[IDX_PAP]) { case STATE_REQ_SENT: /* sppp_cp_change_state() will restart the timer */ sppp_cp_change_state(&pap, sp, STATE_REQ_SENT); break; } splx(s); } /* * That's the timeout handler if we are peer. Since the peer is active, * we need to retransmit our PAP request since it is apparently lost. * XXX We should impose a max counter. */ static void sppp_pap_my_TO(void *cookie) { struct sppp *sp = (struct sppp *)cookie; STDDCL; if (debug) log(LOG_DEBUG, SPP_FMT "pap peer TO\n", SPP_ARGS(ifp)); pap.scr(sp); } static void sppp_pap_tlu(struct sppp *sp) { STDDCL; int x; sp->rst_counter[IDX_PAP] = sp->lcp.max_configure; if (debug) log(LOG_DEBUG, SPP_FMT "%s tlu\n", SPP_ARGS(ifp), pap.name); x = splimp(); /* indicate to LCP that we need to be closed down */ sp->lcp.protos |= (1 << IDX_PAP); if (sp->pp_flags & PP_NEEDAUTH) { /* * Remote is authenticator, but his auth proto didn't * complete yet. Defer the transition to network * phase. */ splx(x); return; } splx(x); sppp_phase_network(sp); } static void sppp_pap_tld(struct sppp *sp) { STDDCL; if (debug) log(LOG_DEBUG, SPP_FMT "pap tld\n", SPP_ARGS(ifp)); UNTIMEOUT(pap.TO, (void *)sp, sp->ch[IDX_PAP]); UNTIMEOUT(sppp_pap_my_TO, (void *)sp, sp->pap_my_to_ch); sp->lcp.protos &= ~(1 << IDX_PAP); lcp.Close(sp); } static void sppp_pap_scr(struct sppp *sp) { u_char idlen, pwdlen; sp->confid[IDX_PAP] = ++sp->pp_seq; pwdlen = sppp_strnlen(sp->myauth.secret, AUTHKEYLEN); idlen = sppp_strnlen(sp->myauth.name, AUTHNAMELEN); sppp_auth_send(&pap, sp, PAP_REQ, sp->confid[IDX_PAP], sizeof idlen, (const char *)&idlen, (size_t)idlen, sp->myauth.name, sizeof pwdlen, (const char *)&pwdlen, (size_t)pwdlen, sp->myauth.secret, 0); } /* * Random miscellaneous functions. */ /* * Send a PAP or CHAP proto packet. * * Varadic function, each of the elements for the ellipsis is of type * ``size_t mlen, const u_char *msg''. Processing will stop iff * mlen == 0. * NOTE: never declare variadic functions with types subject to type * promotion (i.e. u_char). This is asking for big trouble depending * on the architecture you are on... */ static void sppp_auth_send(const struct cp *cp, struct sppp *sp, unsigned int type, unsigned int id, ...) { STDDCL; struct ppp_header *h; struct lcp_header *lh; struct mbuf *m; u_char *p; int len; unsigned int mlen; const char *msg; va_list ap; MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return; m->m_pkthdr.rcvif = 0; h = mtod (m, struct ppp_header*); h->address = PPP_ALLSTATIONS; /* broadcast address */ h->control = PPP_UI; /* Unnumbered Info */ h->protocol = htons(cp->proto); lh = (struct lcp_header*)(h + 1); lh->type = type; lh->ident = id; p = (u_char*) (lh+1); va_start(ap, id); len = 0; while ((mlen = (unsigned int)va_arg(ap, size_t)) != 0) { msg = va_arg(ap, const char *); len += mlen; if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) { va_end(ap); m_freem(m); return; } bcopy(msg, p, mlen); p += mlen; } va_end(ap); m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len; lh->len = htons (LCP_HEADER_LEN + len); if (debug) { log(LOG_DEBUG, SPP_FMT "%s output <%s id=0x%x len=%d", SPP_ARGS(ifp), cp->name, sppp_auth_type_name(cp->proto, lh->type), lh->ident, ntohs(lh->len)); sppp_print_bytes((u_char*) (lh+1), len); log(-1, ">\n"); } if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3)) ifp->if_oerrors++; } /* * Flush interface queue. */ static void sppp_qflush(struct ifqueue *ifq) { struct mbuf *m, *n; n = ifq->ifq_head; while ((m = n)) { n = m->m_act; m_freem (m); } ifq->ifq_head = 0; ifq->ifq_tail = 0; ifq->ifq_len = 0; } /* * Send keepalive packets, every 10 seconds. */ static void sppp_keepalive(void *dummy) { struct sppp *sp; int s; s = splimp(); for (sp=spppq; sp; sp=sp->pp_next) { struct ifnet *ifp = &sp->pp_if; /* Keepalive mode disabled or channel down? */ if (! (sp->pp_flags & PP_KEEPALIVE) || ! (ifp->if_flags & IFF_RUNNING)) continue; /* No keepalive in PPP mode if LCP not opened yet. */ if (sp->pp_mode != IFF_CISCO && sp->pp_phase < PHASE_AUTHENTICATE) continue; if (sp->pp_alivecnt == MAXALIVECNT) { /* No keepalive packets got. Stop the interface. */ printf (SPP_FMT "down\n", SPP_ARGS(ifp)); if_down (ifp); sppp_qflush (&sp->pp_cpq); if (sp->pp_mode != IFF_CISCO) { /* XXX */ /* Shut down the PPP link. */ lcp.Down(sp); /* Initiate negotiation. XXX */ lcp.Up(sp); } } if (sp->pp_alivecnt <= MAXALIVECNT) ++sp->pp_alivecnt; if (sp->pp_mode == IFF_CISCO) sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq, sp->pp_rseq); else if (sp->pp_phase >= PHASE_AUTHENTICATE) { long nmagic = htonl (sp->lcp.magic); sp->lcp.echoid = ++sp->pp_seq; sppp_cp_send (sp, PPP_LCP, ECHO_REQ, sp->lcp.echoid, 4, &nmagic); } } splx(s); TIMEOUT(sppp_keepalive, 0, hz * 10, keepalive_ch); } /* * Get both IP addresses. */ static void sppp_get_ip_addrs(struct sppp *sp, u_long *src, u_long *dst, u_long *srcmask) { struct ifnet *ifp = &sp->pp_if; struct ifaddr *ifa; struct sockaddr_in *si, *sm; u_long ssrc, ddst; sm = NULL; ssrc = ddst = 0L; /* * Pick the first AF_INET address from the list, * aliases don't make any sense on a p2p link anyway. */ si = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) #elif defined(__NetBSD__) || defined (__OpenBSD__) - for (ifa = ifp->if_addrlist.tqh_first; + for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa; - ifa = ifa->ifa_list.tqe_next) + ifa = TAILQ_NEXT(ifa, ifa_list)) #else for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) #endif if (ifa->ifa_addr->sa_family == AF_INET) { si = (struct sockaddr_in *)ifa->ifa_addr; sm = (struct sockaddr_in *)ifa->ifa_netmask; if (si) break; } if (ifa) { if (si && si->sin_addr.s_addr) { ssrc = si->sin_addr.s_addr; if (srcmask) *srcmask = ntohl(sm->sin_addr.s_addr); } si = (struct sockaddr_in *)ifa->ifa_dstaddr; if (si && si->sin_addr.s_addr) ddst = si->sin_addr.s_addr; } if (dst) *dst = ntohl(ddst); if (src) *src = ntohl(ssrc); } /* * Set my IP address. Must be called at splimp. */ static void sppp_set_ip_addr(struct sppp *sp, u_long src) { STDDCL; struct ifaddr *ifa; struct sockaddr_in *si; /* * Pick the first AF_INET address from the list, * aliases don't make any sense on a p2p link anyway. */ si = 0; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) #elif defined(__NetBSD__) || defined (__OpenBSD__) - for (ifa = ifp->if_addrlist.tqh_first; + for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa; - ifa = ifa->ifa_list.tqe_next) + ifa = TAILQ_NEXT(ifa, ifa_list)) #else for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) #endif { if (ifa->ifa_addr->sa_family == AF_INET) { si = (struct sockaddr_in *)ifa->ifa_addr; if (si) break; } } if (ifa && si) { int error; #if __NetBSD_Version__ >= 103080000 struct sockaddr_in new_sin = *si; new_sin.sin_addr.s_addr = htonl(src); error = in_ifinit(ifp, ifatoia(ifa), &new_sin, 1); if(debug && error) { log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: in_ifinit " " failed, error=%d\n", SPP_ARGS(ifp), error); } #else /* delete old route */ error = rtinit(ifa, (int)RTM_DELETE, RTF_HOST); if(debug && error) { log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit DEL failed, error=%d\n", SPP_ARGS(ifp), error); } /* set new address */ si->sin_addr.s_addr = htonl(src); /* add new route */ error = rtinit(ifa, (int)RTM_ADD, RTF_HOST); if (debug && error) { log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit ADD failed, error=%d", SPP_ARGS(ifp), error); } #endif } } static int sppp_params(struct sppp *sp, u_long cmd, void *data) { u_long subcmd; struct ifreq *ifr = (struct ifreq *)data; struct spppreq spr; /* * ifr->ifr_data is supposed to point to a struct spppreq. * Check the cmd word first before attempting to fetch all the * data. */ if ((subcmd = fuword(ifr->ifr_data)) == -1) return EFAULT; if (copyin((caddr_t)ifr->ifr_data, &spr, sizeof spr) != 0) return EFAULT; switch (subcmd) { case SPPPIOGDEFS: if (cmd != SIOCGIFGENERIC) return EINVAL; /* * We copy over the entire current state, but clean * out some of the stuff we don't wanna pass up. * Remember, SIOCGIFGENERIC is unprotected, and can be * called by any user. No need to ever get PAP or * CHAP secrets back to userland anyway. */ bcopy(sp, &spr.defs, sizeof(struct sppp)); bzero(spr.defs.myauth.secret, AUTHKEYLEN); bzero(spr.defs.myauth.challenge, AUTHKEYLEN); bzero(spr.defs.hisauth.secret, AUTHKEYLEN); bzero(spr.defs.hisauth.challenge, AUTHKEYLEN); return copyout(&spr, (caddr_t)ifr->ifr_data, sizeof spr); case SPPPIOSDEFS: if (cmd != SIOCSIFGENERIC) return EINVAL; /* * We have a very specific idea of which fields we allow * being passed back from userland, so to not clobber our * current state. For one, we only allow setting * anything if LCP is in dead phase. Once the LCP * negotiations started, the authentication settings must * not be changed again. (The administrator can force an * ifconfig down in order to get LCP back into dead * phase.) * * Also, we only allow for authentication parameters to be * specified. * * XXX Should allow to set or clear pp_flags. * * Finally, if the respective authentication protocol to * be used is set differently than 0, but the secret is * passed as all zeros, we don't trash the existing secret. * This allows an administrator to change the system name * only without clobbering the secret (which he didn't get * back in a previous SPPPIOGDEFS call). However, the * secrets are cleared if the authentication protocol is * reset to 0. */ if (sp->pp_phase != PHASE_DEAD) return EBUSY; if ((spr.defs.myauth.proto != 0 && spr.defs.myauth.proto != PPP_PAP && spr.defs.myauth.proto != PPP_CHAP) || (spr.defs.hisauth.proto != 0 && spr.defs.hisauth.proto != PPP_PAP && spr.defs.hisauth.proto != PPP_CHAP)) return EINVAL; if (spr.defs.myauth.proto == 0) /* resetting myauth */ bzero(&sp->myauth, sizeof sp->myauth); else { /* setting/changing myauth */ sp->myauth.proto = spr.defs.myauth.proto; bcopy(spr.defs.myauth.name, sp->myauth.name, AUTHNAMELEN); if (spr.defs.myauth.secret[0] != '\0') bcopy(spr.defs.myauth.secret, sp->myauth.secret, AUTHKEYLEN); } if (spr.defs.hisauth.proto == 0) /* resetting hisauth */ bzero(&sp->hisauth, sizeof sp->hisauth); else { /* setting/changing hisauth */ sp->hisauth.proto = spr.defs.hisauth.proto; sp->hisauth.flags = spr.defs.hisauth.flags; bcopy(spr.defs.hisauth.name, sp->hisauth.name, AUTHNAMELEN); if (spr.defs.hisauth.secret[0] != '\0') bcopy(spr.defs.hisauth.secret, sp->hisauth.secret, AUTHKEYLEN); } break; default: return EINVAL; } return 0; } static void sppp_phase_network(struct sppp *sp) { STDDCL; int i; u_long mask; sp->pp_phase = PHASE_NETWORK; if (debug) log(LOG_DEBUG, SPP_FMT "phase %s\n", SPP_ARGS(ifp), sppp_phase_name(sp->pp_phase)); /* Notify NCPs now. */ for (i = 0; i < IDX_COUNT; i++) if ((cps[i])->flags & CP_NCP) (cps[i])->Open(sp); /* Send Up events to all NCPs. */ for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) if (sp->lcp.protos & mask && ((cps[i])->flags & CP_NCP)) (cps[i])->Up(sp); /* if no NCP is starting, all this was in vain, close down */ sppp_lcp_check_and_close(sp); } static const char * sppp_cp_type_name(u_char type) { static char buf[12]; switch (type) { case CONF_REQ: return "conf-req"; case CONF_ACK: return "conf-ack"; case CONF_NAK: return "conf-nak"; case CONF_REJ: return "conf-rej"; case TERM_REQ: return "term-req"; case TERM_ACK: return "term-ack"; case CODE_REJ: return "code-rej"; case PROTO_REJ: return "proto-rej"; case ECHO_REQ: return "echo-req"; case ECHO_REPLY: return "echo-reply"; case DISC_REQ: return "discard-req"; } snprintf (buf, sizeof(buf), "cp/0x%x", type); return buf; } static const char * sppp_auth_type_name(u_short proto, u_char type) { static char buf[12]; switch (proto) { case PPP_CHAP: switch (type) { case CHAP_CHALLENGE: return "challenge"; case CHAP_RESPONSE: return "response"; case CHAP_SUCCESS: return "success"; case CHAP_FAILURE: return "failure"; } case PPP_PAP: switch (type) { case PAP_REQ: return "req"; case PAP_ACK: return "ack"; case PAP_NAK: return "nak"; } } snprintf (buf, sizeof(buf), "auth/0x%x", type); return buf; } static const char * sppp_lcp_opt_name(u_char opt) { static char buf[12]; switch (opt) { case LCP_OPT_MRU: return "mru"; case LCP_OPT_ASYNC_MAP: return "async-map"; case LCP_OPT_AUTH_PROTO: return "auth-proto"; case LCP_OPT_QUAL_PROTO: return "qual-proto"; case LCP_OPT_MAGIC: return "magic"; case LCP_OPT_PROTO_COMP: return "proto-comp"; case LCP_OPT_ADDR_COMP: return "addr-comp"; } snprintf (buf, sizeof(buf), "lcp/0x%x", opt); return buf; } static const char * sppp_ipcp_opt_name(u_char opt) { static char buf[12]; switch (opt) { case IPCP_OPT_ADDRESSES: return "addresses"; case IPCP_OPT_COMPRESSION: return "compression"; case IPCP_OPT_ADDRESS: return "address"; } snprintf (buf, sizeof(buf), "ipcp/0x%x", opt); return buf; } static const char * sppp_state_name(int state) { switch (state) { case STATE_INITIAL: return "initial"; case STATE_STARTING: return "starting"; case STATE_CLOSED: return "closed"; case STATE_STOPPED: return "stopped"; case STATE_CLOSING: return "closing"; case STATE_STOPPING: return "stopping"; case STATE_REQ_SENT: return "req-sent"; case STATE_ACK_RCVD: return "ack-rcvd"; case STATE_ACK_SENT: return "ack-sent"; case STATE_OPENED: return "opened"; } return "illegal"; } static const char * sppp_phase_name(enum ppp_phase phase) { switch (phase) { case PHASE_DEAD: return "dead"; case PHASE_ESTABLISH: return "establish"; case PHASE_TERMINATE: return "terminate"; case PHASE_AUTHENTICATE: return "authenticate"; case PHASE_NETWORK: return "network"; } return "illegal"; } static const char * sppp_proto_name(u_short proto) { static char buf[12]; switch (proto) { case PPP_LCP: return "lcp"; case PPP_IPCP: return "ipcp"; case PPP_PAP: return "pap"; case PPP_CHAP: return "chap"; } snprintf(buf, sizeof(buf), "proto/0x%x", (unsigned)proto); return buf; } static void sppp_print_bytes(const u_char *p, u_short len) { if (len) log(-1, " %*D", len, p, "-"); } static void sppp_print_string(const char *p, u_short len) { u_char c; while (len-- > 0) { c = *p++; /* * Print only ASCII chars directly. RFC 1994 recommends * using only them, but we don't rely on it. */ if (c < ' ' || c > '~') log(-1, "\\x%x", c); else log(-1, "%c", c); } } static const char * sppp_dotted_quad(u_long addr) { static char s[16]; sprintf(s, "%d.%d.%d.%d", (int)((addr >> 24) & 0xff), (int)((addr >> 16) & 0xff), (int)((addr >> 8) & 0xff), (int)(addr & 0xff)); return s; } static int sppp_strnlen(u_char *p, int max) { int len; for (len = 0; len < max && *p; ++p) ++len; return len; } /* a dummy, used to drop uninteresting events */ static void sppp_null(struct sppp *unused) { /* do just nothing */ } Index: head/sys/net/if_stf.c =================================================================== --- head/sys/net/if_stf.c (revision 71958) +++ head/sys/net/if_stf.c (revision 71959) @@ -1,652 +1,652 @@ /* $FreeBSD$ */ /* $KAME: if_stf.c,v 1.42 2000/08/15 07:24:23 itojun Exp $ */ /* * Copyright (C) 2000 WIDE Project. * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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. */ /* * 6to4 interface, based on draft-ietf-ngtrans-6to4-06.txt. * * 6to4 interface is NOT capable of link-layer (I mean, IPv4) multicasting. * There is no address mapping defined from IPv6 multicast address to IPv4 * address. Therefore, we do not have IFF_MULTICAST on the interface. * * Due to the lack of address mapping for link-local addresses, we cannot * throw packets toward link-local addresses (fe80::x). Also, we cannot throw * packets to link-local multicast addresses (ff02::x). * * Here are interesting symptoms due to the lack of link-local address: * * Unicast routing exchange: * - RIPng: Impossible. Uses link-local multicast packet toward ff02::9, * and link-local addresses as nexthop. * - OSPFv6: Impossible. OSPFv6 assumes that there's link-local address * assigned to the link, and makes use of them. Also, HELLO packets use * link-local multicast addresses (ff02::5 and ff02::6). * - BGP4+: Maybe. You can only use global address as nexthop, and global * address as TCP endpoint address. * * Multicast routing protocols: * - PIM: Hello packet cannot be used to discover adjacent PIM routers. * Adjacent PIM routers must be configured manually (is it really spec-wise * correct thing to do?). * * ICMPv6: * - Redirects cannot be used due to the lack of link-local address. * * Starting from 04 draft, the specification suggests how to construct * link-local address for 6to4 interface. * However, it seems to have no real use and does not help the above symptom * much. Even if we assign link-locals to interface, we cannot really * use link-local unicast/multicast on top of 6to4 cloud, and the above * analysis does not change. * * 6to4 interface has security issues. Refer to * http://playground.iijlab.net/i-d/draft-itojun-ipv6-transition-abuse-00.txt * for details. The code tries to filter out some of malicious packets. * Note that there is no way to be 100% secure. */ #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bpf.h" #define NBPFILTER NBPF #include "stf.h" #include "gif.h" /*XXX*/ #if NBPFILTER > 0 #include #endif #if NGIF > 0 #include #endif #if NSTF > 0 #if NSTF != 1 # error only single stf interface allowed #endif #define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002) #define GET_V4(x) ((struct in_addr *)(&(x)->s6_addr16[1])) struct stf_softc { struct ifnet sc_if; /* common area */ union { struct route __sc_ro4; struct route_in6 __sc_ro6; /* just for safety */ } __sc_ro46; #define sc_ro __sc_ro46.__sc_ro4 const struct encaptab *encap_cookie; }; static struct stf_softc *stf; static int nstf; #if NGIF > 0 extern int ip_gif_ttl; /*XXX*/ #else static int ip_gif_ttl = 40; /*XXX*/ #endif extern struct protosw in_stf_protosw; void stfattach __P((void *)); static int stf_encapcheck __P((const struct mbuf *, int, int, void *)); static struct in6_ifaddr *stf_getsrcifa6 __P((struct ifnet *)); static int stf_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); static int stf_checkaddr4 __P((struct in_addr *, struct ifnet *)); static int stf_checkaddr6 __P((struct in6_addr *, struct ifnet *)); static void stf_rtrequest __P((int, struct rtentry *, struct sockaddr *)); static int stf_ioctl __P((struct ifnet *, u_long, caddr_t)); void stfattach(dummy) void *dummy; { struct stf_softc *sc; int i; const struct encaptab *p; nstf = NSTF; stf = malloc(nstf * sizeof(struct stf_softc), M_DEVBUF, M_WAITOK); bzero(stf, nstf * sizeof(struct stf_softc)); sc = stf; /* XXX just in case... */ for (i = 0; i < nstf; i++) { sc = &stf[i]; bzero(sc, sizeof(*sc)); sc->sc_if.if_name = "stf"; sc->sc_if.if_unit = i; p = encap_attach_func(AF_INET, IPPROTO_IPV6, stf_encapcheck, &in_stf_protosw, sc); if (p == NULL) { printf("%s: attach failed\n", if_name(&sc->sc_if)); continue; } sc->encap_cookie = p; sc->sc_if.if_mtu = IPV6_MMTU; sc->sc_if.if_flags = 0; sc->sc_if.if_ioctl = stf_ioctl; sc->sc_if.if_output = stf_output; sc->sc_if.if_type = IFT_STF; sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; if_attach(&sc->sc_if); #if NBPFILTER > 0 #ifdef HAVE_OLD_BPF bpfattach(&sc->sc_if, DLT_NULL, sizeof(u_int)); #else bpfattach(&sc->sc_if.if_bpf, &sc->sc_if, DLT_NULL, sizeof(u_int)); #endif #endif } } PSEUDO_SET(stfattach, if_stf); static int stf_encapcheck(m, off, proto, arg) const struct mbuf *m; int off; int proto; void *arg; { struct ip ip; struct in6_ifaddr *ia6; struct stf_softc *sc; struct in_addr a, b; sc = (struct stf_softc *)arg; if (sc == NULL) return 0; if ((sc->sc_if.if_flags & IFF_UP) == 0) return 0; if (proto != IPPROTO_IPV6) return 0; /* LINTED const cast */ m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip); if (ip.ip_v != 4) return 0; ia6 = stf_getsrcifa6(&sc->sc_if); if (ia6 == NULL) return 0; /* * check if IPv4 dst matches the IPv4 address derived from the * local 6to4 address. * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:... */ if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst, sizeof(ip.ip_dst)) != 0) return 0; /* * check if IPv4 src matches the IPv4 address derived from the * local 6to4 address masked by prefixmask. * success on: src = 10.1.1.1, ia6->ia_addr = 2002:0a00:.../24 * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24 */ bzero(&a, sizeof(a)); a.s_addr = GET_V4(&ia6->ia_addr.sin6_addr)->s_addr; a.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; b = ip.ip_src; b.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; if (a.s_addr != b.s_addr) return 0; /* stf interface makes single side match only */ return 32; } static struct in6_ifaddr * stf_getsrcifa6(ifp) struct ifnet *ifp; { struct ifaddr *ia; struct in_ifaddr *ia4; struct sockaddr_in6 *sin6; struct in_addr in; - for (ia = ifp->if_addrlist.tqh_first; + for (ia = TAILQ_FIRST(&ifp->if_addrlist); ia; - ia = ia->ifa_list.tqe_next) + ia = TAILQ_NEXT(ia, ifa_list)) { if (ia->ifa_addr == NULL) continue; if (ia->ifa_addr->sa_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)ia->ifa_addr; if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) continue; bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in)); for (ia4 = TAILQ_FIRST(&in_ifaddrhead); ia4; ia4 = TAILQ_NEXT(ia4, ia_link)) { if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) break; } if (ia4 == NULL) continue; return (struct in6_ifaddr *)ia; } return NULL; } static int stf_output(ifp, m, dst, rt) struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt; { struct stf_softc *sc; struct sockaddr_in6 *dst6; struct sockaddr_in *dst4; u_int8_t tos; struct ip *ip; struct ip6_hdr *ip6; struct in6_ifaddr *ia6; sc = (struct stf_softc*)ifp; dst6 = (struct sockaddr_in6 *)dst; /* just in case */ if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return ENETDOWN; } /* * If we don't have an ip4 address that match my inner ip6 address, * we shouldn't generate output. Without this check, we'll end up * using wrong IPv4 source. */ ia6 = stf_getsrcifa6(ifp); if (ia6 == NULL) { m_freem(m); return ENETDOWN; } if (m->m_len < sizeof(*ip6)) { m = m_pullup(m, sizeof(*ip6)); if (!m) return ENOBUFS; } ip6 = mtod(m, struct ip6_hdr *); tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); if (m && m->m_len < sizeof(struct ip)) m = m_pullup(m, sizeof(struct ip)); if (m == NULL) return ENOBUFS; ip = mtod(m, struct ip *); bzero(ip, sizeof(*ip)); bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr), &ip->ip_src, sizeof(ip->ip_src)); bcopy(GET_V4(&dst6->sin6_addr), &ip->ip_dst, sizeof(ip->ip_dst)); ip->ip_p = IPPROTO_IPV6; ip->ip_ttl = ip_gif_ttl; /*XXX*/ ip->ip_len = m->m_pkthdr.len; /*host order*/ if (ifp->if_flags & IFF_LINK1) ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos); dst4 = (struct sockaddr_in *)&sc->sc_ro.ro_dst; if (dst4->sin_family != AF_INET || bcmp(&dst4->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)) != 0) { /* cache route doesn't match */ dst4->sin_family = AF_INET; dst4->sin_len = sizeof(struct sockaddr_in); bcopy(&ip->ip_dst, &dst4->sin_addr, sizeof(dst4->sin_addr)); if (sc->sc_ro.ro_rt) { RTFREE(sc->sc_ro.ro_rt); sc->sc_ro.ro_rt = NULL; } } if (sc->sc_ro.ro_rt == NULL) { rtalloc(&sc->sc_ro); if (sc->sc_ro.ro_rt == NULL) { m_freem(m); return ENETUNREACH; } } return ip_output(m, NULL, &sc->sc_ro, 0, NULL); } static int stf_checkaddr4(in, ifp) struct in_addr *in; struct ifnet *ifp; /* incoming interface */ { struct in_ifaddr *ia4; /* * reject packets with the following address: * 224.0.0.0/4 0.0.0.0/8 127.0.0.0/8 255.0.0.0/8 */ if (IN_MULTICAST(ntohl(in->s_addr))) return -1; switch ((ntohl(in->s_addr) & 0xff000000) >> 24) { case 0: case 127: case 255: return -1; } /* * reject packets with broadcast */ for (ia4 = TAILQ_FIRST(&in_ifaddrhead); ia4; ia4 = TAILQ_NEXT(ia4, ia_link)) { if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) continue; if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) return -1; } /* * perform ingress filter */ if (ifp) { struct sockaddr_in sin; struct rtentry *rt; bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); sin.sin_addr = *in; rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); if (!rt) return -1; if (rt->rt_ifp != ifp) { rtfree(rt); return -1; } rtfree(rt); } return 0; } static int stf_checkaddr6(in6, ifp) struct in6_addr *in6; struct ifnet *ifp; /* incoming interface */ { /* * check 6to4 addresses */ if (IN6_IS_ADDR_6TO4(in6)) return stf_checkaddr4(GET_V4(in6), ifp); /* * reject anything that look suspicious. the test is implemented * in ip6_input too, but we check here as well to * (1) reject bad packets earlier, and * (2) to be safe against future ip6_input change. */ if (IN6_IS_ADDR_V4COMPAT(in6) || IN6_IS_ADDR_V4MAPPED(in6)) return -1; return 0; } void #if __STDC__ in_stf_input(struct mbuf *m, ...) #else in_stf_input(m, va_alist) register struct mbuf *m; #endif { int off, proto; struct stf_softc *sc; struct ip *ip; struct ip6_hdr *ip6; u_int8_t otos, itos; int len, isr; struct ifqueue *ifq = NULL; struct ifnet *ifp; va_list ap; va_start(ap, m); off = va_arg(ap, int); proto = va_arg(ap, int); va_end(ap); if (proto != IPPROTO_IPV6) { m_freem(m); return; } ip = mtod(m, struct ip *); sc = (struct stf_softc *)encap_getarg(m); if (sc == NULL || (sc->sc_if.if_flags & IFF_UP) == 0) { m_freem(m); return; } ifp = &sc->sc_if; /* * perform sanity check against outer src/dst. * for source, perform ingress filter as well. */ if (stf_checkaddr4(&ip->ip_dst, NULL) < 0 || stf_checkaddr4(&ip->ip_src, m->m_pkthdr.rcvif) < 0) { m_freem(m); return; } otos = ip->ip_tos; m_adj(m, off); if (m->m_len < sizeof(*ip6)) { m = m_pullup(m, sizeof(*ip6)); if (!m) return; } ip6 = mtod(m, struct ip6_hdr *); /* * perform sanity check against inner src/dst. * for source, perform ingress filter as well. */ if (stf_checkaddr6(&ip6->ip6_dst, NULL) < 0 || stf_checkaddr6(&ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { m_freem(m); return; } itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; if ((ifp->if_flags & IFF_LINK1) != 0) ip_ecn_egress(ECN_ALLOWED, &otos, &itos); ip6->ip6_flow &= ~htonl(0xff << 20); ip6->ip6_flow |= htonl((u_int32_t)itos << 20); m->m_pkthdr.rcvif = ifp; #if NBPFILTER > 0 if (ifp->if_bpf) { /* * We need to prepend the address family as * a four byte field. Cons up a dummy header * to pacify bpf. This is safe because bpf * will only read from the mbuf (i.e., it won't * try to free it or keep a pointer a to it). */ struct mbuf m0; u_int af = AF_INET6; m0.m_next = m; m0.m_len = 4; m0.m_data = (char *)⁡ #ifdef HAVE_OLD_BPF bpf_mtap(ifp, &m0); #else bpf_mtap(ifp->if_bpf, &m0); #endif } #endif /*NBPFILTER > 0*/ /* * Put the packet to the network layer input queue according to the * specified address family. * See net/if_gif.c for possible issues with packet processing * reorder due to extra queueing. */ ifq = &ip6intrq; isr = NETISR_IPV6; len = m->m_pkthdr.len; if (! IF_HANDOFF(ifq, m, NULL)) return; schednetisr(isr); ifp->if_ipackets++; ifp->if_ibytes += len; } /* ARGSUSED */ static void stf_rtrequest(cmd, rt, sa) int cmd; struct rtentry *rt; #if defined(__bsdi__) && _BSDI_VERSION >= 199802 struct rt_addrinfo *sa; #else struct sockaddr *sa; #endif { if (rt) rt->rt_rmx.rmx_mtu = IPV6_MMTU; } static int stf_ioctl(ifp, cmd, data) struct ifnet *ifp; u_long cmd; caddr_t data; { struct ifaddr *ifa; struct ifreq *ifr; struct sockaddr_in6 *sin6; int error; error = 0; switch (cmd) { case SIOCSIFADDR: ifa = (struct ifaddr *)data; if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) { error = EAFNOSUPPORT; break; } sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { ifa->ifa_rtrequest = stf_rtrequest; ifp->if_flags |= IFF_UP; } else error = EINVAL; break; case SIOCADDMULTI: case SIOCDELMULTI: ifr = (struct ifreq *)data; if (ifr && ifr->ifr_addr.sa_family == AF_INET6) ; else error = EAFNOSUPPORT; break; default: error = EINVAL; break; } return error; } #endif /* NSTF > 0 */ Index: head/sys/net/if_tun.c =================================================================== --- head/sys/net/if_tun.c (revision 71958) +++ head/sys/net/if_tun.c (revision 71959) @@ -1,751 +1,751 @@ /* $NetBSD: if_tun.c,v 1.14 1994/06/29 06:36:25 cgd Exp $ */ /* * Copyright (c) 1988, Julian Onions * Nottingham University 1987. * * This source may be freely distributed, however I would be interested * in any changes that are made. * * This driver takes packets off the IP i/f and hands them up to a * user process to have its wicked way with. This driver has it's * roots in a similar driver written by Phil Cockcroft (formerly) at * UCL. This driver is based much more on read/write/poll mode of * operation though. * * $FreeBSD$ */ #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #endif #include #include #include static MALLOC_DEFINE(M_TUN, "tun", "Tunnel Interface"); static void tuncreate __P((dev_t dev)); #define TUNDEBUG if (tundebug) printf static int tundebug = 0; SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, ""); static int tunoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *rt)); static int tunifioctl __P((struct ifnet *, u_long, caddr_t)); static int tuninit __P((struct ifnet *)); static void tunstart __P((struct ifnet *)); static d_open_t tunopen; static d_close_t tunclose; static d_read_t tunread; static d_write_t tunwrite; static d_ioctl_t tunioctl; static d_poll_t tunpoll; #define CDEV_MAJOR 52 static struct cdevsw tun_cdevsw = { /* open */ tunopen, /* close */ tunclose, /* read */ tunread, /* write */ tunwrite, /* ioctl */ tunioctl, /* poll */ tunpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "tun", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static void tun_clone __P((void *arg, char *name, int namelen, dev_t *dev)); static void tun_clone(arg, name, namelen, dev) void *arg; char *name; int namelen; dev_t *dev; { int u; if (*dev != NODEV) return; if (dev_stdclone(name, NULL, "tun", &u) != 1) return; *dev = make_dev(&tun_cdevsw, unit2minor(u), UID_ROOT, GID_WHEEL, 0600, "tun%d", u); } static int tun_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: EVENTHANDLER_REGISTER(dev_clone, tun_clone, 0, 1000); cdevsw_add(&tun_cdevsw); break; case MOD_UNLOAD: printf("if_tun module unload - not possible for this module type\n"); return EINVAL; } return 0; } static moduledata_t tun_mod = { "if_tun", tun_modevent, 0 }; DECLARE_MODULE(if_tun, tun_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); static void tunstart(ifp) struct ifnet *ifp; { struct tun_softc *tp = ifp->if_softc; if (tp->tun_flags & TUN_RWAIT) { tp->tun_flags &= ~TUN_RWAIT; wakeup((caddr_t)tp); } if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio) pgsigio(tp->tun_sigio, SIGIO, 0); selwakeup(&tp->tun_rsel); } static void tuncreate(dev) dev_t dev; { struct tun_softc *sc; struct ifnet *ifp; dev = make_dev(&tun_cdevsw, minor(dev), UID_UUCP, GID_DIALER, 0600, "tun%d", dev2unit(dev)); MALLOC(sc, struct tun_softc *, sizeof(*sc), M_TUN, M_WAITOK | M_ZERO); sc->tun_flags = TUN_INITED; ifp = &sc->tun_if; ifp->if_unit = dev2unit(dev); ifp->if_name = "tun"; ifp->if_mtu = TUNMTU; ifp->if_ioctl = tunifioctl; ifp->if_output = tunoutput; ifp->if_start = tunstart; ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; ifp->if_type = IFT_PPP; ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_softc = sc; if_attach(ifp); bpfattach(ifp, DLT_NULL, sizeof(u_int)); dev->si_drv1 = sc; } /* * tunnel open - must be superuser & the device must be * configured in */ static int tunopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { struct ifnet *ifp; struct tun_softc *tp; tp = dev->si_drv1; if (!tp) { tuncreate(dev); tp = dev->si_drv1; } if (tp->tun_flags & TUN_OPEN) return EBUSY; tp->tun_pid = p->p_pid; ifp = &tp->tun_if; tp->tun_flags |= TUN_OPEN; TUNDEBUG("%s%d: open\n", ifp->if_name, ifp->if_unit); return (0); } /* * tunclose - close the device - mark i/f down & delete * routing info */ static int tunclose(dev, foo, bar, p) dev_t dev; int foo; int bar; struct proc *p; { register int s; struct tun_softc *tp; struct ifnet *ifp; tp = dev->si_drv1; ifp = &tp->tun_if; tp->tun_flags &= ~TUN_OPEN; tp->tun_pid = 0; /* * junk all pending output */ IF_DRAIN(&ifp->if_snd); if (ifp->if_flags & IFF_UP) { s = splimp(); if_down(ifp); splx(s); } if (ifp->if_flags & IFF_RUNNING) { register struct ifaddr *ifa; s = splimp(); /* find internet addresses and delete routes */ - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) if (ifa->ifa_addr->sa_family == AF_INET) rtinit(ifa, (int)RTM_DELETE, tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0); ifp->if_flags &= ~IFF_RUNNING; splx(s); } funsetown(tp->tun_sigio); selwakeup(&tp->tun_rsel); TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit); return (0); } static int tuninit(ifp) struct ifnet *ifp; { struct tun_softc *tp = ifp->if_softc; register struct ifaddr *ifa; int error = 0; TUNDEBUG("%s%d: tuninit\n", ifp->if_name, ifp->if_unit); ifp->if_flags |= IFF_UP | IFF_RUNNING; getmicrotime(&ifp->if_lastchange); - for (ifa = ifp->if_addrhead.tqh_first; ifa; - ifa = ifa->ifa_link.tqe_next) { + for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; + ifa = TAILQ_NEXT(ifa, ifa_link)) { if (ifa->ifa_addr == NULL) error = EFAULT; /* XXX: Should maybe return straight off? */ else { #ifdef INET if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *si; si = (struct sockaddr_in *)ifa->ifa_addr; if (si->sin_addr.s_addr) tp->tun_flags |= TUN_IASET; si = (struct sockaddr_in *)ifa->ifa_dstaddr; if (si && si->sin_addr.s_addr) tp->tun_flags |= TUN_DSTADDR; } #endif } } return (error); } /* * Process an ioctl request. */ int tunifioctl(ifp, cmd, data) struct ifnet *ifp; u_long cmd; caddr_t data; { struct ifreq *ifr = (struct ifreq *)data; struct tun_softc *tp = ifp->if_softc; struct ifstat *ifs; int error = 0, s; s = splimp(); switch(cmd) { case SIOCGIFSTATUS: ifs = (struct ifstat *)data; if (tp->tun_pid) sprintf(ifs->ascii + strlen(ifs->ascii), "\tOpened by PID %d\n", tp->tun_pid); break; case SIOCSIFADDR: error = tuninit(ifp); TUNDEBUG("%s%d: address set, error=%d\n", ifp->if_name, ifp->if_unit, error); break; case SIOCSIFDSTADDR: error = tuninit(ifp); TUNDEBUG("%s%d: destination address set, error=%d\n", ifp->if_name, ifp->if_unit, error); break; case SIOCSIFMTU: ifp->if_mtu = ifr->ifr_mtu; TUNDEBUG("%s%d: mtu set\n", ifp->if_name, ifp->if_unit); break; case SIOCADDMULTI: case SIOCDELMULTI: break; default: error = EINVAL; } splx(s); return (error); } /* * tunoutput - queue packets from higher level ready to put out. */ int tunoutput(ifp, m0, dst, rt) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; struct rtentry *rt; { struct tun_softc *tp = ifp->if_softc; TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit); if ((tp->tun_flags & TUN_READY) != TUN_READY) { TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, ifp->if_unit, tp->tun_flags); m_freem (m0); return EHOSTDOWN; } /* BPF write needs to be handled specially */ if (dst->sa_family == AF_UNSPEC) { dst->sa_family = *(mtod(m0, int *)); m0->m_len -= sizeof(int); m0->m_pkthdr.len -= sizeof(int); m0->m_data += sizeof(int); } if (ifp->if_bpf) { /* * We need to prepend the address family as * a four byte field. Cons up a dummy header * to pacify bpf. This is safe because bpf * will only read from the mbuf (i.e., it won't * try to free it or keep a pointer to it). */ struct mbuf m; uint32_t af = dst->sa_family; m.m_next = m0; m.m_len = 4; m.m_data = (char *)⁡ bpf_mtap(ifp, &m); } /* prepend sockaddr? this may abort if the mbuf allocation fails */ if (tp->tun_flags & TUN_LMODE) { /* allocate space for sockaddr */ M_PREPEND(m0, dst->sa_len, M_DONTWAIT); /* if allocation failed drop packet */ if (m0 == NULL) { ifp->if_iqdrops++; ifp->if_oerrors++; return (ENOBUFS); } else { bcopy(dst, m0->m_data, dst->sa_len); } } if (tp->tun_flags & TUN_IFHEAD) { /* Prepend the address family */ M_PREPEND(m0, 4, M_DONTWAIT); /* if allocation failed drop packet */ if (m0 == NULL) { ifp->if_iqdrops++; ifp->if_oerrors++; return ENOBUFS; } else *(u_int32_t *)m0->m_data = htonl(dst->sa_family); } else { #ifdef INET if (dst->sa_family != AF_INET) #endif { m_freem(m0); return EAFNOSUPPORT; } } if (! IF_HANDOFF(&ifp->if_snd, m0, ifp)) { ifp->if_collisions++; return ENOBUFS; } ifp->if_opackets++; return 0; } /* * the cdevsw interface is now pretty minimal. */ static int tunioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { int s; int error; struct tun_softc *tp = dev->si_drv1; struct tuninfo *tunp; switch (cmd) { case TUNSIFINFO: tunp = (struct tuninfo *)data; if (tunp->mtu < IF_MINMTU) return (EINVAL); if (tp->tun_if.if_mtu != tunp->mtu && (error = suser(p)) != 0) return (error); tp->tun_if.if_mtu = tunp->mtu; tp->tun_if.if_type = tunp->type; tp->tun_if.if_baudrate = tunp->baudrate; break; case TUNGIFINFO: tunp = (struct tuninfo *)data; tunp->mtu = tp->tun_if.if_mtu; tunp->type = tp->tun_if.if_type; tunp->baudrate = tp->tun_if.if_baudrate; break; case TUNSDEBUG: tundebug = *(int *)data; break; case TUNGDEBUG: *(int *)data = tundebug; break; case TUNSLMODE: if (*(int *)data) { tp->tun_flags |= TUN_LMODE; tp->tun_flags &= ~TUN_IFHEAD; } else tp->tun_flags &= ~TUN_LMODE; break; case TUNSIFHEAD: if (*(int *)data) { tp->tun_flags |= TUN_IFHEAD; tp->tun_flags &= ~TUN_LMODE; } else tp->tun_flags &= ~TUN_IFHEAD; break; case TUNGIFHEAD: *(int *)data = (tp->tun_flags & TUN_IFHEAD) ? 1 : 0; break; case TUNSIFMODE: /* deny this if UP */ if (tp->tun_if.if_flags & IFF_UP) return(EBUSY); switch (*(int *)data) { case IFF_POINTOPOINT: tp->tun_if.if_flags |= IFF_POINTOPOINT; tp->tun_if.if_flags &= ~IFF_BROADCAST; break; case IFF_BROADCAST: tp->tun_if.if_flags &= ~IFF_POINTOPOINT; tp->tun_if.if_flags |= IFF_BROADCAST; break; default: return(EINVAL); } break; case TUNSIFPID: tp->tun_pid = curproc->p_pid; break; case FIONBIO: break; case FIOASYNC: if (*(int *)data) tp->tun_flags |= TUN_ASYNC; else tp->tun_flags &= ~TUN_ASYNC; break; case FIONREAD: s = splimp(); if (tp->tun_if.if_snd.ifq_head) { struct mbuf *mb = tp->tun_if.if_snd.ifq_head; for( *(int *)data = 0; mb != 0; mb = mb->m_next) *(int *)data += mb->m_len; } else *(int *)data = 0; splx(s); break; case FIOSETOWN: return (fsetown(*(int *)data, &tp->tun_sigio)); case FIOGETOWN: *(int *)data = fgetown(tp->tun_sigio); return (0); /* This is deprecated, FIOSETOWN should be used instead. */ case TIOCSPGRP: return (fsetown(-(*(int *)data), &tp->tun_sigio)); /* This is deprecated, FIOGETOWN should be used instead. */ case TIOCGPGRP: *(int *)data = -fgetown(tp->tun_sigio); return (0); default: return (ENOTTY); } return (0); } /* * The cdevsw read interface - reads a packet at a time, or at * least as much of a packet as can be read. */ static int tunread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = &tp->tun_if; struct mbuf *m, *m0; int error=0, len, s; TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit); if ((tp->tun_flags & TUN_READY) != TUN_READY) { TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, ifp->if_unit, tp->tun_flags); return EHOSTDOWN; } tp->tun_flags &= ~TUN_RWAIT; s = splimp(); do { IF_DEQUEUE(&ifp->if_snd, m0); if (m0 == 0) { if (flag & IO_NDELAY) { splx(s); return EWOULDBLOCK; } tp->tun_flags |= TUN_RWAIT; if((error = tsleep((caddr_t)tp, PCATCH | (PZERO + 1), "tunread", 0)) != 0) { splx(s); return error; } } } while (m0 == 0); splx(s); while (m0 && uio->uio_resid > 0 && error == 0) { len = min(uio->uio_resid, m0->m_len); if (len == 0) break; error = uiomove(mtod(m0, caddr_t), len, uio); MFREE(m0, m); m0 = m; } if (m0) { TUNDEBUG("Dropping mbuf\n"); m_freem(m0); } return error; } /* * the cdevsw write interface - an atomic write is a packet - or else! */ static int tunwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = &tp->tun_if; struct mbuf *top, **mp, *m; int error=0, tlen, mlen; uint32_t family; TUNDEBUG("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit); if (uio->uio_resid == 0) return 0; if (uio->uio_resid < 0 || uio->uio_resid > TUNMRU) { TUNDEBUG("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit, uio->uio_resid); return EIO; } tlen = uio->uio_resid; /* get a header mbuf */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return ENOBUFS; mlen = MHLEN; top = 0; mp = ⊤ while (error == 0 && uio->uio_resid > 0) { m->m_len = min(mlen, uio->uio_resid); error = uiomove(mtod (m, caddr_t), m->m_len, uio); *mp = m; mp = &m->m_next; if (uio->uio_resid > 0) { MGET (m, M_DONTWAIT, MT_DATA); if (m == 0) { error = ENOBUFS; break; } mlen = MLEN; } } if (error) { if (top) m_freem (top); ifp->if_ierrors++; return error; } top->m_pkthdr.len = tlen; top->m_pkthdr.rcvif = ifp; if (ifp->if_bpf) { if (tp->tun_flags & TUN_IFHEAD) { /* * Conveniently, we already have a 4-byte address * family prepended to our packet ! * Inconveniently, it's in the wrong byte order ! */ if ((top = m_pullup(top, sizeof(family))) == NULL) return ENOBUFS; *mtod(top, u_int32_t *) = ntohl(*mtod(top, u_int32_t *)); bpf_mtap(ifp, top); *mtod(top, u_int32_t *) = htonl(*mtod(top, u_int32_t *)); } else { /* * We need to prepend the address family as * a four byte field. Cons up a dummy header * to pacify bpf. This is safe because bpf * will only read from the mbuf (i.e., it won't * try to free it or keep a pointer to it). */ struct mbuf m; uint32_t af = AF_INET; m.m_next = top; m.m_len = 4; m.m_data = (char *)⁡ bpf_mtap(ifp, &m); } } if (tp->tun_flags & TUN_IFHEAD) { if (top->m_len < sizeof(family) && (top = m_pullup(top, sizeof(family))) == NULL) return ENOBUFS; family = ntohl(*mtod(top, u_int32_t *)); m_adj(top, sizeof(family)); } else family = AF_INET; ifp->if_ibytes += top->m_pkthdr.len; ifp->if_ipackets++; return family_enqueue(family, top); } /* * tunpoll - the poll interface, this is only useful on reads * really. The write detect always returns true, write never blocks * anyway, it either accepts the packet or drops it. */ static int tunpoll(dev, events, p) dev_t dev; int events; struct proc *p; { int s; struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = &tp->tun_if; int revents = 0; s = splimp(); TUNDEBUG("%s%d: tunpoll\n", ifp->if_name, ifp->if_unit); if (events & (POLLIN | POLLRDNORM)) { if (ifp->if_snd.ifq_len > 0) { TUNDEBUG("%s%d: tunpoll q=%d\n", ifp->if_name, ifp->if_unit, ifp->if_snd.ifq_len); revents |= events & (POLLIN | POLLRDNORM); } else { TUNDEBUG("%s%d: tunpoll waiting\n", ifp->if_name, ifp->if_unit); selrecord(p, &tp->tun_rsel); } } if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLOUT | POLLWRNORM); splx(s); return (revents); } Index: head/sys/net/if_vlan.c =================================================================== --- head/sys/net/if_vlan.c (revision 71958) +++ head/sys/net/if_vlan.c (revision 71959) @@ -1,551 +1,551 @@ /* * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * * $FreeBSD$ */ /* * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. * Might be extended some day to also handle IEEE 802.1p priority * tagging. This is sort of sneaky in the implementation, since * we need to pretend to be enough of an Ethernet implementation * to make arp work. The way we do this is by telling everyone * that we are an Ethernet, and then catch the packets that * ether_output() left on our output queue when it calls * if_start(), rewrite them for use by the real outgoing interface, * and ask it to send them. * * * XXX It's incorrect to assume that we must always kludge up * headers on the physical device's behalf: some devices support * VLAN tag insersion and extraction in firmware. For these cases, * one can change the behavior of the vlan interface by setting * the LINK0 flag on it (that is setting the vlan interface's LINK0 * flag, _not_ the parent's LINK0 flag; we try to leave the parent * alone). If the interface as the LINK0 flag set, then it will * not modify the ethernet header on output because the parent * can do that for itself. On input, the parent can call vlan_input_tag() * directly in order to supply us with an incoming mbuf and the vlan * tag value that goes with it. */ #include "vlan.h" #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); u_int vlan_proto = ETHERTYPE_VLAN; SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto, 0, "Ethernet protocol used for VLAN encapsulation"); static struct ifvlan ifv_softc[NVLAN]; static void vlan_start(struct ifnet *ifp); static void vlan_ifinit(void *foo); static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); static int vlan_setmulti(struct ifnet *ifp); static int vlan_unconfig(struct ifnet *ifp); static int vlan_config(struct ifvlan *ifv, struct ifnet *p); /* * Program our multicast filter. What we're actually doing is * programming the multicast filter of the parent. This has the * side effect of causing the parent interface to receive multicast * traffic that it doesn't really want, which ends up being discarded * later by the upper protocol layers. Unfortunately, there's no way * to avoid this: there really is only one physical interface. */ static int vlan_setmulti(struct ifnet *ifp) { struct ifnet *ifp_p; struct ifmultiaddr *ifma, *rifma = NULL; struct ifvlan *sc; struct vlan_mc_entry *mc = NULL; struct sockaddr_dl sdl; int error; /* Find the parent. */ sc = ifp->if_softc; ifp_p = sc->ifv_p; sdl.sdl_len = ETHER_ADDR_LEN; sdl.sdl_family = AF_LINK; /* First, remove any existing filter entries. */ - while(sc->vlan_mc_listhead.slh_first != NULL) { - mc = sc->vlan_mc_listhead.slh_first; + while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { + mc = SLIST_FIRST(&sc->vlan_mc_listhead); bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); if (error) return(error); SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); free(mc, M_DEVBUF); } /* Now program new ones. */ - for (ifma = ifp->if_multiaddrs.lh_first; - ifma != NULL;ifma = ifma->ifma_link.le_next) { + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); + ifma != NULL;ifma = LIST_NEXT(ifma, ifma_link)) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_NOWAIT); bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), (char *)&mc->mc_addr, ETHER_ADDR_LEN); SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); if (error) return(error); } return(0); } static void vlaninit(void) { int i; for (i = 0; i < NVLAN; i++) { struct ifnet *ifp = &ifv_softc[i].ifv_if; ifp->if_softc = &ifv_softc[i]; ifp->if_name = "vlan"; ifp->if_unit = i; /* NB: flags are not set here */ ifp->if_linkmib = &ifv_softc[i].ifv_mib; ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib; /* NB: mtu is not set here */ ifp->if_init = vlan_ifinit; ifp->if_start = vlan_start; ifp->if_ioctl = vlan_ioctl; ifp->if_output = ether_output; ifp->if_snd.ifq_maxlen = ifqmaxlen; ether_ifattach(ifp, ETHER_BPF_SUPPORTED); /* Now undo some of the damage... */ ifp->if_data.ifi_type = IFT_8021_VLAN; ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; ifp->if_resolvemulti = 0; } } static int vlan_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: vlaninit(); break; case MOD_UNLOAD: printf("if_vlan module unload - not possible for this module type\n"); return EINVAL; } return 0; } static moduledata_t vlan_mod = { "if_vlan", vlan_modevent, 0 }; DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); static void vlan_ifinit(void *foo) { return; } static void vlan_start(struct ifnet *ifp) { struct ifvlan *ifv; struct ifnet *p; struct ether_vlan_header *evl; struct mbuf *m; ifv = ifp->if_softc; p = ifv->ifv_p; ifp->if_flags |= IFF_OACTIVE; for (;;) { IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) break; if (ifp->if_bpf) bpf_mtap(ifp, m); /* * If the LINK0 flag is set, it means the underlying interface * can do VLAN tag insertion itself and doesn't require us to * create a special header for it. In this case, we just pass * the packet along. However, we need some way to tell the * interface where the packet came from so that it knows how * to find the VLAN tag to use, so we set the rcvif in the * mbuf header to our ifnet. * * Note: we also set the M_PROTO1 flag in the mbuf to let * the parent driver know that the rcvif pointer is really * valid. We need to do this because sometimes mbufs will * be allocated by other parts of the system that contain * garbage in the rcvif pointer. Using the M_PROTO1 flag * lets the driver perform a proper sanity check and avoid * following potentially bogus rcvif pointers off into * never-never land. */ if (ifp->if_flags & IFF_LINK0) { m->m_pkthdr.rcvif = ifp; m->m_flags |= M_PROTO1; } else { M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); if (m == NULL) { printf("vlan%d: M_PREPEND failed", ifp->if_unit); ifp->if_ierrors++; continue; } /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN); if (m == NULL) { printf("vlan%d: m_pullup failed", ifp->if_unit); ifp->if_ierrors++; continue; } /* * Transform the Ethernet header into an Ethernet header * with 802.1Q encapsulation. */ bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), sizeof(struct ether_header)); evl = mtod(m, struct ether_vlan_header *); evl->evl_proto = evl->evl_encap_proto; evl->evl_encap_proto = htons(vlan_proto); evl->evl_tag = htons(ifv->ifv_tag); #ifdef DEBUG printf("vlan_start: %*D\n", sizeof *evl, (char *)evl, ":"); #endif } /* * Send it, precisely as ether_output() would have. * We are already running at splimp. */ if (IF_HANDOFF(&p->if_snd, m, p)) ifp->if_opackets++; else ifp->if_oerrors++; } ifp->if_flags &= ~IFF_OACTIVE; return; } int vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t) { int i; struct ifvlan *ifv; for (i = 0; i < NVLAN; i++) { ifv = &ifv_softc[i]; if (ifv->ifv_tag == t) break; } if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { m_free(m); return -1; /* So the parent can take note */ } /* * Having found a valid vlan interface corresponding to * the given source interface and vlan tag, run the * the real packet through ethert_input(). */ m->m_pkthdr.rcvif = &ifv->ifv_if; ifv->ifv_if.if_ipackets++; ether_input(&ifv->ifv_if, eh, m); return 0; } int vlan_input(struct ether_header *eh, struct mbuf *m) { int i; struct ifvlan *ifv; for (i = 0; i < NVLAN; i++) { ifv = &ifv_softc[i]; if (m->m_pkthdr.rcvif == ifv->ifv_p && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) == ifv->ifv_tag)) break; } if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { m_freem(m); return -1; /* so ether_input can take note */ } /* * Having found a valid vlan interface corresponding to * the given source interface and vlan tag, remove the * encapsulation, and run the real packet through * ether_input() a second time (it had better be * reentrant!). */ m->m_pkthdr.rcvif = &ifv->ifv_if; eh->ether_type = mtod(m, u_int16_t *)[1]; m->m_data += EVL_ENCAPLEN; m->m_len -= EVL_ENCAPLEN; m->m_pkthdr.len -= EVL_ENCAPLEN; ifv->ifv_if.if_ipackets++; ether_input(&ifv->ifv_if, eh, m); return 0; } static int vlan_config(struct ifvlan *ifv, struct ifnet *p) { struct ifaddr *ifa1, *ifa2; struct sockaddr_dl *sdl1, *sdl2; if (p->if_data.ifi_type != IFT_ETHER) return EPROTONOSUPPORT; if (ifv->ifv_p) return EBUSY; ifv->ifv_p = p; if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) ifv->ifv_if.if_mtu = p->if_mtu; else ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; /* * Preserve the state of the LINK0 flag for ourselves. */ ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0)); /* * Set up our ``Ethernet address'' to reflect the underlying * physical interface's. */ ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1]; ifa2 = ifnet_addrs[p->if_index - 1]; sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; sdl1->sdl_type = IFT_ETHER; sdl1->sdl_alen = ETHER_ADDR_LEN; bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); return 0; } static int vlan_unconfig(struct ifnet *ifp) { struct ifaddr *ifa; struct sockaddr_dl *sdl; struct vlan_mc_entry *mc; struct ifvlan *ifv; struct ifnet *p; int error; ifv = ifp->if_softc; p = ifv->ifv_p; /* * Since the interface is being unconfigured, we need to * empty the list of multicast groups that we may have joined * while we were alive and remove them from the parent's list * as well. */ - while(ifv->vlan_mc_listhead.slh_first != NULL) { + while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { struct sockaddr_dl sdl; sdl.sdl_len = ETHER_ADDR_LEN; sdl.sdl_family = AF_LINK; - mc = ifv->vlan_mc_listhead.slh_first; + mc = SLIST_FIRST(&ifv->vlan_mc_listhead); bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); error = if_delmulti(p, (struct sockaddr *)&sdl); error = if_delmulti(ifp, (struct sockaddr *)&sdl); if (error) return(error); SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); free(mc, M_DEVBUF); } /* Disconnect from parent. */ ifv->ifv_p = NULL; ifv->ifv_if.if_mtu = ETHERMTU; /* Clear our MAC address. */ ifa = ifnet_addrs[ifv->ifv_if.if_index - 1]; sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; bzero(LLADDR(sdl), ETHER_ADDR_LEN); bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); return 0; } static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ifaddr *ifa; struct ifnet *p; struct ifreq *ifr; struct ifvlan *ifv; struct vlanreq vlr; int error = 0; ifr = (struct ifreq *)data; ifa = (struct ifaddr *)data; ifv = ifp->if_softc; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: arp_ifinit(&ifv->ifv_ac, ifa); break; #endif default: break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) &ifr->ifr_data; bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. * This is bogus. The underlying interface might support * jumbo frames. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; case SIOCSETVLAN: error = copyin(ifr->ifr_data, &vlr, sizeof vlr); if (error) break; if (vlr.vlr_parent[0] == '\0') { vlan_unconfig(ifp); if_down(ifp); ifp->if_flags &= ~(IFF_UP|IFF_RUNNING); break; } p = ifunit(vlr.vlr_parent); if (p == 0) { error = ENOENT; break; } error = vlan_config(ifv, p); if (error) break; ifv->ifv_tag = vlr.vlr_tag; ifp->if_flags |= IFF_RUNNING; break; case SIOCGETVLAN: bzero(&vlr, sizeof vlr); if (ifv->ifv_p) { snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); vlr.vlr_tag = ifv->ifv_tag; } error = copyout(&vlr, ifr->ifr_data, sizeof vlr); break; case SIOCSIFFLAGS: /* * We don't support promiscuous mode * right now because it would require help from the * underlying drivers, which hasn't been implemented. */ if (ifr->ifr_flags & (IFF_PROMISC)) { ifp->if_flags &= ~(IFF_PROMISC); error = EINVAL; } break; case SIOCADDMULTI: case SIOCDELMULTI: error = vlan_setmulti(ifp); break; default: error = EINVAL; } return error; } Index: head/sys/net/pfil.c =================================================================== --- head/sys/net/pfil.c (revision 71958) +++ head/sys/net/pfil.c (revision 71959) @@ -1,175 +1,175 @@ /* $FreeBSD$ */ /* * Copyright (c) 1996 Matthew R. Green * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include static void pfil_init __P((struct pfil_head *)); static int pfil_list_add(pfil_list_t *, int (*) __P((void *, int, struct ifnet *, int, struct mbuf **)), int); static int pfil_list_remove(pfil_list_t *, int (*) __P((void *, int, struct ifnet *, int, struct mbuf **))); static void pfil_init(ph) struct pfil_head *ph; { TAILQ_INIT(&ph->ph_in); TAILQ_INIT(&ph->ph_out); ph->ph_init = 1; } /* * pfil_add_hook() adds a function to the packet filter hook. the * flags are: * PFIL_IN call me on incoming packets * PFIL_OUT call me on outgoing packets * PFIL_ALL call me on all of the above * PFIL_WAITOK OK to call malloc with M_WAITOK. */ int pfil_add_hook(func, flags, ph) int (*func) __P((void *, int, struct ifnet *, int, struct mbuf **)); int flags; struct pfil_head *ph; { int err = 0; if (ph->ph_init == 0) pfil_init(ph); if (flags & PFIL_IN) err = pfil_list_add(&ph->ph_in, func, flags & ~PFIL_OUT); if (err) return err; if (flags & PFIL_OUT) err = pfil_list_add(&ph->ph_out, func, flags & ~PFIL_IN); if (err) { if (flags & PFIL_IN) pfil_list_remove(&ph->ph_in, func); return err; } return 0; } static int pfil_list_add(list, func, flags) pfil_list_t *list; int (*func) __P((void *, int, struct ifnet *, int, struct mbuf **)); int flags; { struct packet_filter_hook *pfh; pfh = (struct packet_filter_hook *)malloc(sizeof(*pfh), M_IFADDR, flags & PFIL_WAITOK ? M_WAITOK : M_NOWAIT); if (pfh == NULL) return ENOMEM; pfh->pfil_func = func; /* * insert the input list in reverse order of the output list * so that the same path is followed in or out of the kernel. */ if (flags & PFIL_IN) TAILQ_INSERT_HEAD(list, pfh, pfil_link); else TAILQ_INSERT_TAIL(list, pfh, pfil_link); return 0; } /* * pfil_remove_hook removes a specific function from the packet filter * hook list. */ int pfil_remove_hook(func, flags, ph) int (*func) __P((void *, int, struct ifnet *, int, struct mbuf **)); int flags; struct pfil_head *ph; { int err = 0; if (ph->ph_init == 0) pfil_init(ph); if (flags & PFIL_IN) err = pfil_list_remove(&ph->ph_in, func); if ((err == 0) && (flags & PFIL_OUT)) err = pfil_list_remove(&ph->ph_out, func); return err; } /* * pfil_list_remove is an internal function that takes a function off the * specified list. */ static int pfil_list_remove(list, func) pfil_list_t *list; int (*func) __P((void *, int, struct ifnet *, int, struct mbuf **)); { struct packet_filter_hook *pfh; - for (pfh = list->tqh_first; pfh; pfh = pfh->pfil_link.tqe_next) + for (pfh = list->tqh_first; pfh; pfh = TAILQ_NEXT(pfh, pfil_link)) if (pfh->pfil_func == func) { TAILQ_REMOVE(list, pfh, pfil_link); free(pfh, M_IFADDR); return 0; } return ENOENT; } struct packet_filter_hook * pfil_hook_get(flag, ph) int flag; struct pfil_head *ph; { if (ph->ph_init != 0) switch (flag) { case PFIL_IN: - return (ph->ph_in.tqh_first); + return (TAILQ_FIRST(&ph->ph_in)); case PFIL_OUT: - return (ph->ph_out.tqh_first); + return (TAILQ_FIRST(&ph->ph_out)); } return NULL; } Index: head/sys/net/rtsock.c =================================================================== --- head/sys/net/rtsock.c (revision 71958) +++ head/sys/net/rtsock.c (revision 71959) @@ -1,1006 +1,1006 @@ /* * Copyright (c) 1988, 1991, 1993 * The 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. * * @(#)rtsock.c 8.5 (Berkeley) 11/2/94 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_RTABLE, "routetbl", "routing tables"); static struct sockaddr route_dst = { 2, PF_ROUTE, }; static struct sockaddr route_src = { 2, PF_ROUTE, }; static struct sockaddr sa_zero = { sizeof(sa_zero), AF_INET, }; static struct sockproto route_proto = { PF_ROUTE, }; struct walkarg { int w_tmemsize; int w_op, w_arg; caddr_t w_tmem; struct sysctl_req *w_req; }; static struct mbuf * rt_msg1 __P((int, struct rt_addrinfo *)); static int rt_msg2 __P((int, struct rt_addrinfo *, caddr_t, struct walkarg *)); static int rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *)); static int sysctl_dumpentry __P((struct radix_node *rn, void *vw)); static int sysctl_iflist __P((int af, struct walkarg *w)); static int route_output __P((struct mbuf *, struct socket *)); static void rt_setmetrics __P((u_long, struct rt_metrics *, struct rt_metrics *)); /* Sleazy use of local variables throughout file, warning!!!! */ #define dst info.rti_info[RTAX_DST] #define gate info.rti_info[RTAX_GATEWAY] #define netmask info.rti_info[RTAX_NETMASK] #define genmask info.rti_info[RTAX_GENMASK] #define ifpaddr info.rti_info[RTAX_IFP] #define ifaaddr info.rti_info[RTAX_IFA] #define brdaddr info.rti_info[RTAX_BRD] /* * It really doesn't make any sense at all for this code to share much * with raw_usrreq.c, since its functionality is so restricted. XXX */ static int rts_abort(struct socket *so) { int s, error; s = splnet(); error = raw_usrreqs.pru_abort(so); splx(s); return error; } /* pru_accept is EOPNOTSUPP */ static int rts_attach(struct socket *so, int proto, struct proc *p) { struct rawcb *rp; int s, error; if (sotorawcb(so) != 0) return EISCONN; /* XXX panic? */ /* XXX */ MALLOC(rp, struct rawcb *, sizeof *rp, M_PCB, M_WAITOK | M_ZERO); if (rp == 0) return ENOBUFS; /* * The splnet() is necessary to block protocols from sending * error notifications (like RTM_REDIRECT or RTM_LOSING) while * this PCB is extant but incompletely initialized. * Probably we should try to do more of this work beforehand and * eliminate the spl. */ s = splnet(); so->so_pcb = (caddr_t)rp; error = raw_usrreqs.pru_attach(so, proto, p); rp = sotorawcb(so); if (error) { splx(s); free(rp, M_PCB); return error; } switch(rp->rcb_proto.sp_protocol) { case AF_INET: route_cb.ip_count++; break; case AF_INET6: route_cb.ip6_count++; break; case AF_IPX: route_cb.ipx_count++; break; case AF_NS: route_cb.ns_count++; break; } rp->rcb_faddr = &route_src; route_cb.any_count++; soisconnected(so); so->so_options |= SO_USELOOPBACK; splx(s); return 0; } static int rts_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { int s, error; s = splnet(); error = raw_usrreqs.pru_bind(so, nam, p); /* xxx just EINVAL */ splx(s); return error; } static int rts_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { int s, error; s = splnet(); error = raw_usrreqs.pru_connect(so, nam, p); /* XXX just EINVAL */ splx(s); return error; } /* pru_connect2 is EOPNOTSUPP */ /* pru_control is EOPNOTSUPP */ static int rts_detach(struct socket *so) { struct rawcb *rp = sotorawcb(so); int s, error; s = splnet(); if (rp != 0) { switch(rp->rcb_proto.sp_protocol) { case AF_INET: route_cb.ip_count--; break; case AF_INET6: route_cb.ip6_count--; break; case AF_IPX: route_cb.ipx_count--; break; case AF_NS: route_cb.ns_count--; break; } route_cb.any_count--; } error = raw_usrreqs.pru_detach(so); splx(s); return error; } static int rts_disconnect(struct socket *so) { int s, error; s = splnet(); error = raw_usrreqs.pru_disconnect(so); splx(s); return error; } /* pru_listen is EOPNOTSUPP */ static int rts_peeraddr(struct socket *so, struct sockaddr **nam) { int s, error; s = splnet(); error = raw_usrreqs.pru_peeraddr(so, nam); splx(s); return error; } /* pru_rcvd is EOPNOTSUPP */ /* pru_rcvoob is EOPNOTSUPP */ static int rts_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct proc *p) { int s, error; s = splnet(); error = raw_usrreqs.pru_send(so, flags, m, nam, control, p); splx(s); return error; } /* pru_sense is null */ static int rts_shutdown(struct socket *so) { int s, error; s = splnet(); error = raw_usrreqs.pru_shutdown(so); splx(s); return error; } static int rts_sockaddr(struct socket *so, struct sockaddr **nam) { int s, error; s = splnet(); error = raw_usrreqs.pru_sockaddr(so, nam); splx(s); return error; } static struct pr_usrreqs route_usrreqs = { rts_abort, pru_accept_notsupp, rts_attach, rts_bind, rts_connect, pru_connect2_notsupp, pru_control_notsupp, rts_detach, rts_disconnect, pru_listen_notsupp, rts_peeraddr, pru_rcvd_notsupp, pru_rcvoob_notsupp, rts_send, pru_sense_null, rts_shutdown, rts_sockaddr, sosend, soreceive, sopoll }; /*ARGSUSED*/ static int route_output(m, so) register struct mbuf *m; struct socket *so; { register struct rt_msghdr *rtm = 0; register struct rtentry *rt = 0; struct rtentry *saved_nrt = 0; struct radix_node_head *rnh; struct rt_addrinfo info; int len, error = 0; struct ifnet *ifp = 0; struct ifaddr *ifa = 0; #define senderr(e) { error = e; goto flush;} if (m == 0 || ((m->m_len < sizeof(long)) && (m = m_pullup(m, sizeof(long))) == 0)) return (ENOBUFS); if ((m->m_flags & M_PKTHDR) == 0) panic("route_output"); len = m->m_pkthdr.len; if (len < sizeof(*rtm) || len != mtod(m, struct rt_msghdr *)->rtm_msglen) { dst = 0; senderr(EINVAL); } R_Malloc(rtm, struct rt_msghdr *, len); if (rtm == 0) { dst = 0; senderr(ENOBUFS); } m_copydata(m, 0, len, (caddr_t)rtm); if (rtm->rtm_version != RTM_VERSION) { dst = 0; senderr(EPROTONOSUPPORT); } rtm->rtm_pid = curproc->p_pid; info.rti_addrs = rtm->rtm_addrs; if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, &info)) { dst = 0; senderr(EINVAL); } if (dst == 0 || (dst->sa_family >= AF_MAX) || (gate != 0 && (gate->sa_family >= AF_MAX))) senderr(EINVAL); if (genmask) { struct radix_node *t; t = rn_addmask((caddr_t)genmask, 0, 1); if (t && Bcmp((caddr_t *)genmask + 1, (caddr_t *)t->rn_key + 1, *(u_char *)t->rn_key - 1) == 0) genmask = (struct sockaddr *)(t->rn_key); else senderr(ENOBUFS); } switch (rtm->rtm_type) { case RTM_ADD: if (gate == 0) senderr(EINVAL); error = rtrequest(RTM_ADD, dst, gate, netmask, rtm->rtm_flags, &saved_nrt); if (error == 0 && saved_nrt) { rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, &saved_nrt->rt_rmx); saved_nrt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits); saved_nrt->rt_rmx.rmx_locks |= (rtm->rtm_inits & rtm->rtm_rmx.rmx_locks); saved_nrt->rt_refcnt--; saved_nrt->rt_genmask = genmask; } break; case RTM_DELETE: error = rtrequest(RTM_DELETE, dst, gate, netmask, rtm->rtm_flags, &saved_nrt); if (error == 0) { if ((rt = saved_nrt)) rt->rt_refcnt++; goto report; } break; case RTM_GET: case RTM_CHANGE: case RTM_LOCK: if ((rnh = rt_tables[dst->sa_family]) == 0) { senderr(EAFNOSUPPORT); } else if ((rt = (struct rtentry *) rnh->rnh_lookup(dst, netmask, rnh)) != NULL) rt->rt_refcnt++; else senderr(ESRCH); switch(rtm->rtm_type) { case RTM_GET: report: dst = rt_key(rt); gate = rt->rt_gateway; netmask = rt_mask(rt); genmask = rt->rt_genmask; if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) { ifp = rt->rt_ifp; if (ifp) { - ifpaddr = ifp->if_addrhead.tqh_first->ifa_addr; + ifpaddr = TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr; ifaaddr = rt->rt_ifa->ifa_addr; rtm->rtm_index = ifp->if_index; } else { ifpaddr = 0; ifaaddr = 0; } } len = rt_msg2(rtm->rtm_type, &info, (caddr_t)0, (struct walkarg *)0); if (len > rtm->rtm_msglen) { struct rt_msghdr *new_rtm; R_Malloc(new_rtm, struct rt_msghdr *, len); if (new_rtm == 0) senderr(ENOBUFS); Bcopy(rtm, new_rtm, rtm->rtm_msglen); Free(rtm); rtm = new_rtm; } (void)rt_msg2(rtm->rtm_type, &info, (caddr_t)rtm, (struct walkarg *)0); rtm->rtm_flags = rt->rt_flags; rtm->rtm_rmx = rt->rt_rmx; rtm->rtm_addrs = info.rti_addrs; break; case RTM_CHANGE: if (gate && (error = rt_setgate(rt, rt_key(rt), gate))) senderr(error); /* * If they tried to change things but didn't specify * the required gateway, then just use the old one. * This can happen if the user tries to change the * flags on the default route without changing the * default gateway. Changing flags still doesn't work. */ if ((rt->rt_flags & RTF_GATEWAY) && !gate) gate = rt->rt_gateway; /* new gateway could require new ifaddr, ifp; flags may also be different; ifp may be specified by ll sockaddr when protocol address is ambiguous */ if (ifpaddr && (ifa = ifa_ifwithnet(ifpaddr)) && (ifp = ifa->ifa_ifp) && (ifaaddr || gate)) ifa = ifaof_ifpforaddr(ifaaddr ? ifaaddr : gate, ifp); else if ((ifaaddr && (ifa = ifa_ifwithaddr(ifaaddr))) || (gate && (ifa = ifa_ifwithroute(rt->rt_flags, rt_key(rt), gate)))) ifp = ifa->ifa_ifp; if (ifa) { register struct ifaddr *oifa = rt->rt_ifa; if (oifa != ifa) { if (oifa && oifa->ifa_rtrequest) oifa->ifa_rtrequest(RTM_DELETE, rt, gate); IFAFREE(rt->rt_ifa); rt->rt_ifa = ifa; ifa->ifa_refcnt++; rt->rt_ifp = ifp; } } rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, &rt->rt_rmx); if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest) rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, gate); if (genmask) rt->rt_genmask = genmask; /* * Fall into */ case RTM_LOCK: rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits); rt->rt_rmx.rmx_locks |= (rtm->rtm_inits & rtm->rtm_rmx.rmx_locks); break; } break; default: senderr(EOPNOTSUPP); } flush: if (rtm) { if (error) rtm->rtm_errno = error; else rtm->rtm_flags |= RTF_DONE; } if (rt) rtfree(rt); { register struct rawcb *rp = 0; /* * Check to see if we don't want our own messages. */ if ((so->so_options & SO_USELOOPBACK) == 0) { if (route_cb.any_count <= 1) { if (rtm) Free(rtm); m_freem(m); return (error); } /* There is another listener, so construct message */ rp = sotorawcb(so); } if (rtm) { m_copyback(m, 0, rtm->rtm_msglen, (caddr_t)rtm); Free(rtm); } if (rp) rp->rcb_proto.sp_family = 0; /* Avoid us */ if (dst) route_proto.sp_protocol = dst->sa_family; raw_input(m, &route_proto, &route_src, &route_dst); if (rp) rp->rcb_proto.sp_family = PF_ROUTE; } return (error); } static void rt_setmetrics(which, in, out) u_long which; register struct rt_metrics *in, *out; { #define metric(f, e) if (which & (f)) out->e = in->e; metric(RTV_RPIPE, rmx_recvpipe); metric(RTV_SPIPE, rmx_sendpipe); metric(RTV_SSTHRESH, rmx_ssthresh); metric(RTV_RTT, rmx_rtt); metric(RTV_RTTVAR, rmx_rttvar); metric(RTV_HOPCOUNT, rmx_hopcount); metric(RTV_MTU, rmx_mtu); metric(RTV_EXPIRE, rmx_expire); #undef metric } #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) /* * Extract the addresses of the passed sockaddrs. * Do a little sanity checking so as to avoid bad memory references. * This data is derived straight from userland. */ static int rt_xaddrs(cp, cplim, rtinfo) register caddr_t cp, cplim; register struct rt_addrinfo *rtinfo; { register struct sockaddr *sa; register int i; bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info)); for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { if ((rtinfo->rti_addrs & (1 << i)) == 0) continue; sa = (struct sockaddr *)cp; /* * It won't fit. */ if ( (cp + sa->sa_len) > cplim ) { return (EINVAL); } /* * there are no more.. quit now * If there are more bits, they are in error. * I've seen this. route(1) can evidently generate these. * This causes kernel to core dump. * for compatibility, If we see this, point to a safe address. */ if (sa->sa_len == 0) { rtinfo->rti_info[i] = &sa_zero; return (0); /* should be EINVAL but for compat */ } /* accept it */ rtinfo->rti_info[i] = sa; ADVANCE(cp, sa); } return (0); } static struct mbuf * rt_msg1(type, rtinfo) int type; register struct rt_addrinfo *rtinfo; { register struct rt_msghdr *rtm; register struct mbuf *m; register int i; register struct sockaddr *sa; int len, dlen; m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == 0) return (m); switch (type) { case RTM_DELADDR: case RTM_NEWADDR: len = sizeof(struct ifa_msghdr); break; case RTM_DELMADDR: case RTM_NEWMADDR: len = sizeof(struct ifma_msghdr); break; case RTM_IFINFO: len = sizeof(struct if_msghdr); break; default: len = sizeof(struct rt_msghdr); } if (len > MHLEN) panic("rt_msg1"); m->m_pkthdr.len = m->m_len = len; m->m_pkthdr.rcvif = 0; rtm = mtod(m, struct rt_msghdr *); bzero((caddr_t)rtm, len); for (i = 0; i < RTAX_MAX; i++) { if ((sa = rtinfo->rti_info[i]) == NULL) continue; rtinfo->rti_addrs |= (1 << i); dlen = ROUNDUP(sa->sa_len); m_copyback(m, len, dlen, (caddr_t)sa); len += dlen; } if (m->m_pkthdr.len != len) { m_freem(m); return (NULL); } rtm->rtm_msglen = len; rtm->rtm_version = RTM_VERSION; rtm->rtm_type = type; return (m); } static int rt_msg2(type, rtinfo, cp, w) int type; register struct rt_addrinfo *rtinfo; caddr_t cp; struct walkarg *w; { register int i; int len, dlen, second_time = 0; caddr_t cp0; rtinfo->rti_addrs = 0; again: switch (type) { case RTM_DELADDR: case RTM_NEWADDR: len = sizeof(struct ifa_msghdr); break; case RTM_IFINFO: len = sizeof(struct if_msghdr); break; default: len = sizeof(struct rt_msghdr); } cp0 = cp; if (cp0) cp += len; for (i = 0; i < RTAX_MAX; i++) { register struct sockaddr *sa; if ((sa = rtinfo->rti_info[i]) == 0) continue; rtinfo->rti_addrs |= (1 << i); dlen = ROUNDUP(sa->sa_len); if (cp) { bcopy((caddr_t)sa, cp, (unsigned)dlen); cp += dlen; } len += dlen; } if (cp == 0 && w != NULL && !second_time) { register struct walkarg *rw = w; if (rw->w_req) { if (rw->w_tmemsize < len) { if (rw->w_tmem) free(rw->w_tmem, M_RTABLE); rw->w_tmem = (caddr_t) malloc(len, M_RTABLE, M_NOWAIT); if (rw->w_tmem) rw->w_tmemsize = len; } if (rw->w_tmem) { cp = rw->w_tmem; second_time = 1; goto again; } } } if (cp) { register struct rt_msghdr *rtm = (struct rt_msghdr *)cp0; rtm->rtm_version = RTM_VERSION; rtm->rtm_type = type; rtm->rtm_msglen = len; } return (len); } /* * This routine is called to generate a message from the routing * socket indicating that a redirect has occured, a routing lookup * has failed, or that a protocol has detected timeouts to a particular * destination. */ void rt_missmsg(type, rtinfo, flags, error) int type, flags, error; register struct rt_addrinfo *rtinfo; { register struct rt_msghdr *rtm; register struct mbuf *m; struct sockaddr *sa = rtinfo->rti_info[RTAX_DST]; if (route_cb.any_count == 0) return; m = rt_msg1(type, rtinfo); if (m == 0) return; rtm = mtod(m, struct rt_msghdr *); rtm->rtm_flags = RTF_DONE | flags; rtm->rtm_errno = error; rtm->rtm_addrs = rtinfo->rti_addrs; route_proto.sp_protocol = sa ? sa->sa_family : 0; raw_input(m, &route_proto, &route_src, &route_dst); } /* * This routine is called to generate a message from the routing * socket indicating that the status of a network interface has changed. */ void rt_ifmsg(ifp) register struct ifnet *ifp; { register struct if_msghdr *ifm; struct mbuf *m; struct rt_addrinfo info; if (route_cb.any_count == 0) return; bzero((caddr_t)&info, sizeof(info)); m = rt_msg1(RTM_IFINFO, &info); if (m == 0) return; ifm = mtod(m, struct if_msghdr *); ifm->ifm_index = ifp->if_index; ifm->ifm_flags = (u_short)ifp->if_flags; ifm->ifm_data = ifp->if_data; ifm->ifm_addrs = 0; route_proto.sp_protocol = 0; raw_input(m, &route_proto, &route_src, &route_dst); } /* * This is called to generate messages from the routing socket * indicating a network interface has had addresses associated with it. * if we ever reverse the logic and replace messages TO the routing * socket indicate a request to configure interfaces, then it will * be unnecessary as the routing socket will automatically generate * copies of it. */ void rt_newaddrmsg(cmd, ifa, error, rt) int cmd, error; register struct ifaddr *ifa; register struct rtentry *rt; { struct rt_addrinfo info; struct sockaddr *sa = 0; int pass; struct mbuf *m = 0; struct ifnet *ifp = ifa->ifa_ifp; if (route_cb.any_count == 0) return; for (pass = 1; pass < 3; pass++) { bzero((caddr_t)&info, sizeof(info)); if ((cmd == RTM_ADD && pass == 1) || (cmd == RTM_DELETE && pass == 2)) { register struct ifa_msghdr *ifam; int ncmd = cmd == RTM_ADD ? RTM_NEWADDR : RTM_DELADDR; ifaaddr = sa = ifa->ifa_addr; - ifpaddr = ifp->if_addrhead.tqh_first->ifa_addr; + ifpaddr = TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr; netmask = ifa->ifa_netmask; brdaddr = ifa->ifa_dstaddr; if ((m = rt_msg1(ncmd, &info)) == NULL) continue; ifam = mtod(m, struct ifa_msghdr *); ifam->ifam_index = ifp->if_index; ifam->ifam_metric = ifa->ifa_metric; ifam->ifam_flags = ifa->ifa_flags; ifam->ifam_addrs = info.rti_addrs; } if ((cmd == RTM_ADD && pass == 2) || (cmd == RTM_DELETE && pass == 1)) { register struct rt_msghdr *rtm; if (rt == 0) continue; netmask = rt_mask(rt); dst = sa = rt_key(rt); gate = rt->rt_gateway; if ((m = rt_msg1(cmd, &info)) == NULL) continue; rtm = mtod(m, struct rt_msghdr *); rtm->rtm_index = ifp->if_index; rtm->rtm_flags |= rt->rt_flags; rtm->rtm_errno = error; rtm->rtm_addrs = info.rti_addrs; } route_proto.sp_protocol = sa ? sa->sa_family : 0; raw_input(m, &route_proto, &route_src, &route_dst); } } /* * This is the analogue to the rt_newaddrmsg which performs the same * function but for multicast group memberhips. This is easier since * there is no route state to worry about. */ void rt_newmaddrmsg(cmd, ifma) int cmd; struct ifmultiaddr *ifma; { struct rt_addrinfo info; struct mbuf *m = 0; struct ifnet *ifp = ifma->ifma_ifp; struct ifma_msghdr *ifmam; if (route_cb.any_count == 0) return; bzero((caddr_t)&info, sizeof(info)); ifaaddr = ifma->ifma_addr; - if (ifp && ifp->if_addrhead.tqh_first) - ifpaddr = ifp->if_addrhead.tqh_first->ifa_addr; + if (ifp && TAILQ_FIRST(&ifp->if_addrhead)) + ifpaddr = TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr; else ifpaddr = NULL; /* * If a link-layer address is present, present it as a ``gateway'' * (similarly to how ARP entries, e.g., are presented). */ gate = ifma->ifma_lladdr; if ((m = rt_msg1(cmd, &info)) == NULL) return; ifmam = mtod(m, struct ifma_msghdr *); ifmam->ifmam_index = ifp->if_index; ifmam->ifmam_addrs = info.rti_addrs; route_proto.sp_protocol = ifma->ifma_addr->sa_family; raw_input(m, &route_proto, &route_src, &route_dst); } /* * This is used in dumping the kernel table via sysctl(). */ int sysctl_dumpentry(rn, vw) struct radix_node *rn; void *vw; { register struct walkarg *w = vw; register struct rtentry *rt = (struct rtentry *)rn; int error = 0, size; struct rt_addrinfo info; if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg)) return 0; bzero((caddr_t)&info, sizeof(info)); dst = rt_key(rt); gate = rt->rt_gateway; netmask = rt_mask(rt); genmask = rt->rt_genmask; size = rt_msg2(RTM_GET, &info, 0, w); if (w->w_req && w->w_tmem) { register struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem; rtm->rtm_flags = rt->rt_flags; rtm->rtm_use = rt->rt_use; rtm->rtm_rmx = rt->rt_rmx; rtm->rtm_index = rt->rt_ifp->if_index; rtm->rtm_errno = rtm->rtm_pid = rtm->rtm_seq = 0; rtm->rtm_addrs = info.rti_addrs; error = SYSCTL_OUT(w->w_req, (caddr_t)rtm, size); return (error); } return (error); } int sysctl_iflist(af, w) int af; register struct walkarg *w; { register struct ifnet *ifp; register struct ifaddr *ifa; struct rt_addrinfo info; int len, error = 0; bzero((caddr_t)&info, sizeof(info)); - for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) { + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_link)) { if (w->w_arg && w->w_arg != ifp->if_index) continue; - ifa = ifp->if_addrhead.tqh_first; + ifa = TAILQ_FIRST(&ifp->if_addrhead); ifpaddr = ifa->ifa_addr; len = rt_msg2(RTM_IFINFO, &info, (caddr_t)0, w); ifpaddr = 0; if (w->w_req && w->w_tmem) { register struct if_msghdr *ifm; ifm = (struct if_msghdr *)w->w_tmem; ifm->ifm_index = ifp->if_index; ifm->ifm_flags = (u_short)ifp->if_flags; ifm->ifm_data = ifp->if_data; ifm->ifm_addrs = info.rti_addrs; error = SYSCTL_OUT(w->w_req,(caddr_t)ifm, len); if (error) return (error); } - while ((ifa = ifa->ifa_link.tqe_next) != 0) { + while ((ifa = TAILQ_NEXT(ifa, ifa_link)) != 0) { if (af && af != ifa->ifa_addr->sa_family) continue; if (curproc->p_prison && prison_if(curproc, ifa->ifa_addr)) continue; ifaaddr = ifa->ifa_addr; netmask = ifa->ifa_netmask; brdaddr = ifa->ifa_dstaddr; len = rt_msg2(RTM_NEWADDR, &info, 0, w); if (w->w_req && w->w_tmem) { register struct ifa_msghdr *ifam; ifam = (struct ifa_msghdr *)w->w_tmem; ifam->ifam_index = ifa->ifa_ifp->if_index; ifam->ifam_flags = ifa->ifa_flags; ifam->ifam_metric = ifa->ifa_metric; ifam->ifam_addrs = info.rti_addrs; error = SYSCTL_OUT(w->w_req, w->w_tmem, len); if (error) return (error); } } ifaaddr = netmask = brdaddr = 0; } return (0); } static int sysctl_rtsock(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; register struct radix_node_head *rnh; int i, s, error = EINVAL; u_char af; struct walkarg w; name ++; namelen--; if (req->newptr) return (EPERM); if (namelen != 3) return (EINVAL); af = name[0]; Bzero(&w, sizeof(w)); w.w_op = name[1]; w.w_arg = name[2]; w.w_req = req; s = splnet(); switch (w.w_op) { case NET_RT_DUMP: case NET_RT_FLAGS: for (i = 1; i <= AF_MAX; i++) if ((rnh = rt_tables[i]) && (af == 0 || af == i) && (error = rnh->rnh_walktree(rnh, sysctl_dumpentry, &w))) break; break; case NET_RT_IFLIST: error = sysctl_iflist(af, &w); } splx(s); if (w.w_tmem) free(w.w_tmem, M_RTABLE); return (error); } SYSCTL_NODE(_net, PF_ROUTE, routetable, CTLFLAG_RD, sysctl_rtsock, ""); /* * Definitions of protocols supported in the ROUTE domain. */ extern struct domain routedomain; /* or at least forward */ static struct protosw routesw[] = { { SOCK_RAW, &routedomain, 0, PR_ATOMIC|PR_ADDR, 0, route_output, raw_ctlinput, 0, 0, raw_init, 0, 0, 0, &route_usrreqs } }; static struct domain routedomain = { PF_ROUTE, "route", 0, 0, 0, routesw, &routesw[sizeof(routesw)/sizeof(routesw[0])] }; DOMAIN_SET(route);