Index: head/sys/net/bridge.c =================================================================== --- head/sys/net/bridge.c (revision 130548) +++ head/sys/net/bridge.c (revision 130549) @@ -1,1255 +1,1258 @@ /* * Copyright (c) 1998-2002 Luigi Rizzo * * Work partly supported by: Cisco Systems, Inc. - NSITE lab, RTP, NC * * 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 * interfaces, including VLANs (others are still usable for routing). * A FreeBSD host can implement multiple logical bridges, called * "clusters". Each cluster is made of a set of interfaces, and * identified by a "cluster-id" which is a number in the range 1..2^16-1. * * Bridging is enabled by the sysctl variable * net.link.ether.bridge.enable * the grouping of interfaces into clusters is done with * net.link.ether.bridge.config * containing a list of interfaces each optionally followed by * a colon and the cluster it belongs to (1 is the default). * Separators can be * spaces, commas or tabs, e.g. * net.link.ether.bridge.config="fxp0:2 fxp1:2 dc0 dc1:1" * Optionally bridged packets can be passed through the firewall, * this is controlled by the variable * net.link.ether.bridge.ipfw * * For each cluster there is a descriptor (cluster_softc) storing * the following data structures: * - a hash table with the MAC address and destination interface for each * known node. The table is indexed using a hash of the source address. * - an array with the MAC addresses of the interfaces used in the cluster. * * Input packets are tapped near the beginning of ether_input(), and * analysed by 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 by bridge_dst_lookup(), * and then forwarding is done by bdg_forward(). * * 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. * * 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 per cluster is supposed to have addresses set (but * there are no substantial problems if you set addresses for none or * 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. * - be very careful when bridging VLANs * - loop detection is still not very robust. */ #include "opt_pfil_hooks.h" #include #include #include #include #include #include /* for net/if.h */ #include /* string functions */ #include #include #include #include #include #include /* for struct arpcom */ #include #include #include #include #include #include #ifdef PFIL_HOOKS #include #include #endif #include #include #include #include /*--------------------*/ #define ETHER_ADDR_COPY(_dst,_src) bcopy(_src, _dst, ETHER_ADDR_LEN) #define ETHER_ADDR_EQ(_a1,_a2) (bcmp(_a1, _a2, ETHER_ADDR_LEN) == 0) /* * For each cluster, source MAC addresses are stored into a hash * table which locates the port they reside on. */ #define HASH_SIZE 8192 /* Table size, must be a power of 2 */ typedef struct hash_table { /* each entry. */ struct ifnet * name; u_char etheraddr[ETHER_ADDR_LEN]; u_int16_t used; /* also, padding */ } bdg_hash_table ; /* * The hash function applied to MAC addresses. Out of the 6 bytes, * the last ones tend to vary more. Since we are on a little endian machine, * we have to do some gimmick... */ #define HASH_FN(addr) ( \ ntohs( ((u_int16_t *)addr)[1] ^ ((u_int16_t *)addr)[2] ) & (HASH_SIZE -1)) /* * This is the data structure where local addresses are stored. */ struct bdg_addr { u_char etheraddr[ETHER_ADDR_LEN]; u_int16_t _padding; }; /* * The configuration of each cluster includes the cluster id, a pointer to * the hash table, and an array of local MAC addresses (of size "ports"). */ struct cluster_softc { u_int16_t cluster_id; u_int16_t ports; bdg_hash_table *ht; struct bdg_addr *my_macs; /* local MAC addresses */ }; extern struct protosw inetsw[]; /* from netinet/ip_input.c */ static int n_clusters; /* number of clusters */ static struct cluster_softc *clusters; static struct mtx bdg_mtx; #define BDG_LOCK_INIT() mtx_init(&bdg_mtx, "bridge", NULL, MTX_DEF) #define BDG_LOCK_DESTROY() mtx_destroy(&bdg_mtx) #define BDG_LOCK() mtx_lock(&bdg_mtx) #define BDG_UNLOCK() mtx_unlock(&bdg_mtx) #define BDG_LOCK_ASSERT() mtx_assert(&bdg_mtx, MA_OWNED) #define BDG_MUTED(ifp) (ifp2sc[ifp->if_index].flags & IFF_MUTE) #define BDG_MUTE(ifp) ifp2sc[ifp->if_index].flags |= IFF_MUTE #define BDG_CLUSTER(ifp) (ifp2sc[ifp->if_index].cluster) #define BDG_SAMECLUSTER(ifp,src) \ (src == NULL || BDG_CLUSTER(ifp) == BDG_CLUSTER(src) ) #ifdef __i386__ #define BDG_MATCH(a,b) ( \ ((u_int16_t *)(a))[2] == ((u_int16_t *)(b))[2] && \ *((u_int32_t *)(a)) == *((u_int32_t *)(b)) ) #define IS_ETHER_BROADCAST(a) ( \ *((u_int32_t *)(a)) == 0xffffffff && \ ((u_int16_t *)(a))[2] == 0xffff ) #else /* for machines that do not support unaligned access */ #define BDG_MATCH(a,b) ETHER_ADDR_EQ(a,b) #define IS_ETHER_BROADCAST(a) ETHER_ADDR_EQ(a,"\377\377\377\377\377\377") #endif SYSCTL_DECL(_net_link_ether); SYSCTL_NODE(_net_link_ether, OID_AUTO, bridge, CTLFLAG_RD, 0, "Bridge parameters"); static char bridge_version[] = "031224"; SYSCTL_STRING(_net_link_ether_bridge, OID_AUTO, version, CTLFLAG_RD, bridge_version, 0, "software version"); #define BRIDGE_DEBUG #ifdef BRIDGE_DEBUG int bridge_debug = 0; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, debug, CTLFLAG_RW, &bridge_debug, 0, "control debugging printfs"); #define DPRINTF(X) if (bridge_debug) printf X #else #define DPRINTF(X) #endif #ifdef BRIDGE_TIMING /* * For timing-related 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 static int bdg_fw_avg; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, fw_avg, CTLFLAG_RW, &bdg_fw_avg, 0,"Cycle counter avg"); static int bdg_fw_ticks; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, fw_ticks, CTLFLAG_RW, &bdg_fw_ticks, 0,"Cycle counter item"); static int bdg_fw_count; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, fw_count, CTLFLAG_RW, &bdg_fw_count, 0,"Cycle counter count"); #else #define DDB(x) #endif static int bdginit(void); static void parse_bdg_cfg(void); static int bdg_ipf; /* IPFilter enabled in bridge */ SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, ipf, CTLFLAG_RW, &bdg_ipf, 0,"Pass bridged pkts through IPFilter"); static int bdg_ipfw; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, ipfw, CTLFLAG_RW, &bdg_ipfw,0,"Pass bridged pkts through firewall"); static int bdg_copy; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, copy, CTLFLAG_RW, &bdg_copy, 0, "Force packet copy in bdg_forward"); int bdg_ipfw_drops; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, ipfw_drop, CTLFLAG_RW, &bdg_ipfw_drops,0,""); int bdg_ipfw_colls; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, ipfw_collisions, CTLFLAG_RW, &bdg_ipfw_colls,0,""); static int bdg_thru; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, packets, CTLFLAG_RW, &bdg_thru, 0, "Packets through bridge"); static int bdg_dropped; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, dropped, CTLFLAG_RW, &bdg_dropped, 0, "Packets dropped in bdg_forward"); static int bdg_predict; SYSCTL_INT(_net_link_ether_bridge, OID_AUTO, predict, CTLFLAG_RW, &bdg_predict, 0, "Correctly predicted header location"); #ifdef BRIDGE_DEBUG static char *bdg_dst_names[] = { "BDG_NULL ", "BDG_BCAST ", "BDG_MCAST ", "BDG_LOCAL ", "BDG_DROP ", "BDG_UNKNOWN ", "BDG_IN ", "BDG_OUT ", "BDG_FORWARD " }; #endif /* BRIDGE_DEBUG */ /* * System initialization */ static struct bdg_stats bdg_stats ; SYSCTL_STRUCT(_net_link_ether_bridge, OID_AUTO, stats, CTLFLAG_RD, &bdg_stats, bdg_stats, "bridge statistics"); static struct callout bdg_callout; /* * Add an interface to a cluster, possibly creating a new entry in * the cluster table. This requires reallocation of the table and * updating pointers in ifp2sc. */ static struct cluster_softc * add_cluster(u_int16_t cluster_id, struct ifnet *ifp) { struct cluster_softc *c = NULL; int i; BDG_LOCK_ASSERT(); for (i = 0; i < n_clusters ; i++) if (clusters[i].cluster_id == cluster_id) goto found; /* Not found, need to reallocate */ c = malloc((1+n_clusters) * sizeof (*c), M_IFADDR, M_NOWAIT | M_ZERO); if (c == NULL) {/* malloc failure */ printf("-- bridge: cannot add new cluster\n"); goto bad; } c[n_clusters].ht = (struct hash_table *) malloc(HASH_SIZE * sizeof(struct hash_table), M_IFADDR, M_NOWAIT | M_ZERO); if (c[n_clusters].ht == NULL) { printf("-- bridge: cannot allocate hash table for new cluster\n"); goto bad; } c[n_clusters].my_macs = (struct bdg_addr *) malloc(BDG_MAX_PORTS * sizeof(struct bdg_addr), M_IFADDR, M_NOWAIT | M_ZERO); if (c[n_clusters].my_macs == NULL) { printf("-- bridge: cannot allocate mac addr table for new cluster\n"); free(c[n_clusters].ht, M_IFADDR); goto bad; } c[n_clusters].cluster_id = cluster_id; c[n_clusters].ports = 0; /* * now copy old descriptors here */ if (n_clusters > 0) { for (i=0; i < n_clusters; i++) c[i] = clusters[i]; /* * and finally update pointers in ifp2sc */ for (i = 0 ; i < if_index && i < BDG_MAX_PORTS; i++) if (ifp2sc[i].cluster != NULL) ifp2sc[i].cluster = c + (ifp2sc[i].cluster - clusters); free(clusters, M_IFADDR); } clusters = c; i = n_clusters; /* index of cluster entry */ n_clusters++; found: c = clusters + i; /* the right cluster ... */ ETHER_ADDR_COPY(c->my_macs[c->ports].etheraddr, IFP2AC(ifp)->ac_enaddr); c->ports++; return c; bad: if (c) free(c, M_IFADDR); return NULL; } /* * Turn off bridging, by clearing promisc mode on the interface, * marking the interface as unused, and clearing the name in the * stats entry. * Also dispose the hash tables associated with the clusters. */ static void bridge_off(void) { struct ifnet *ifp ; int i; BDG_LOCK_ASSERT(); DPRINTF(("%s: n_clusters %d\n", __func__, n_clusters)); IFNET_RLOCK(); TAILQ_FOREACH(ifp, &ifnet, if_link) { struct bdg_softc *b; if (ifp->if_index >= BDG_MAX_PORTS) continue; /* make sure we do not go beyond the end */ b = &ifp2sc[ifp->if_index]; if ( b->flags & IFF_BDG_PROMISC ) { ifpromisc(ifp, 0); b->flags &= ~(IFF_BDG_PROMISC|IFF_MUTE) ; DPRINTF(("%s: %s promisc OFF if_flags 0x%x " "bdg_flags 0x%x\n", __func__, ifp->if_xname, ifp->if_flags, b->flags)); } b->flags &= ~(IFF_USED) ; b->cluster = NULL; bdg_stats.s[ifp->if_index].name[0] = '\0'; } IFNET_RUNLOCK(); /* flush_tables */ for (i=0; i < n_clusters; i++) { free(clusters[i].ht, M_IFADDR); free(clusters[i].my_macs, M_IFADDR); } if (clusters != NULL) free(clusters, M_IFADDR); clusters = NULL; n_clusters =0; } /* * set promisc mode on the interfaces we use. */ static void bridge_on(void) { struct ifnet *ifp ; BDG_LOCK_ASSERT(); IFNET_RLOCK(); TAILQ_FOREACH(ifp, &ifnet, if_link) { struct bdg_softc *b = &ifp2sc[ifp->if_index]; if ( !(b->flags & IFF_USED) ) continue ; if ( !( ifp->if_flags & IFF_UP) ) { if_up(ifp); } if ( !(b->flags & IFF_BDG_PROMISC) ) { (void) ifpromisc(ifp, 1); b->flags |= IFF_BDG_PROMISC ; DPRINTF(("%s: %s promisc ON if_flags 0x%x bdg_flags 0x%x\n", __func__, ifp->if_xname, ifp->if_flags, b->flags)); } if (b->flags & IFF_MUTE) { DPRINTF(("%s: unmuting %s\n", __func__, ifp->if_xname)); b->flags &= ~IFF_MUTE; } } IFNET_RUNLOCK(); } static char bridge_cfg[1024]; /* NB: in BSS so initialized to zero */ /** * reconfigure bridge. * This is also done every time we attach or detach an interface. * Main use is to make sure that we do not bridge on some old * (ejected) device. So, it would be really useful to have a * pointer to the modified device as an argument. Without it, we * have to scan all interfaces. */ static void reconfigure_bridge_locked(void) { BDG_LOCK_ASSERT(); bridge_off(); if (do_bridge) { if (if_index >= BDG_MAX_PORTS) { printf("-- sorry too many interfaces (%d, max is %d)," " disabling bridging\n", if_index, BDG_MAX_PORTS); do_bridge = 0; return; } parse_bdg_cfg(); bridge_on(); } } static void reconfigure_bridge(void) { BDG_LOCK(); reconfigure_bridge_locked(); BDG_UNLOCK(); } /* * parse the config string, set IFF_USED, name and cluster_id * for all interfaces found. * The config string is a list of "if[:cluster]" with * a number of possible separators (see "sep"). In particular the * use of the space lets you set bridge_cfg with the output from * "ifconfig -l" */ static void parse_bdg_cfg(void) { char *p, *beg; int l, cluster; static const char *sep = ", \t"; BDG_LOCK_ASSERT(); for (p = bridge_cfg; *p ; p++) { struct ifnet *ifp; int found = 0; char c; if (index(sep, *p)) /* skip separators */ continue ; /* names are lowercase and digits */ for ( beg = p ; islower(*p) || isdigit(*p) ; p++ ) ; l = p - beg ; /* length of name string */ if (l == 0) /* invalid name */ break ; if ( *p != ':' ) /* no ':', assume default cluster 1 */ cluster = 1 ; else /* fetch cluster */ cluster = strtoul( p+1, &p, 10); c = *p; *p = '\0'; /* * now search in interface list for a matching name */ IFNET_RLOCK(); /* could sleep XXX */ TAILQ_FOREACH(ifp, &ifnet, if_link) { if (!strncmp(beg, ifp->if_xname, max(l, strlen(ifp->if_xname)))) { struct bdg_softc *b = &ifp2sc[ifp->if_index]; if (ifp->if_type != IFT_ETHER && ifp->if_type != IFT_L2VLAN) { printf("%s is not an ethernet, continue\n", ifp->if_xname); continue; } if (b->flags & IFF_USED) { printf("%s already used, skipping\n", ifp->if_xname); break; } b->cluster = add_cluster(htons(cluster), ifp); b->flags |= IFF_USED ; snprintf(bdg_stats.s[ifp->if_index].name, sizeof(bdg_stats.s[ifp->if_index].name), "%s:%d", ifp->if_xname, cluster); DPRINTF(("%s: found %s next c %d\n", __func__, bdg_stats.s[ifp->if_index].name, c)); found = 1; break ; } } IFNET_RUNLOCK(); if (!found) printf("interface %s Not found in bridge\n", beg); *p = c; if (c == '\0') break; /* no more */ } } /* * handler for net.link.ether.bridge */ static int sysctl_bdg(SYSCTL_HANDLER_ARGS) { int enable = do_bridge; int error; error = sysctl_handle_int(oidp, &enable, 0, req); BDG_LOCK(); if (enable != do_bridge) { do_bridge = enable; reconfigure_bridge_locked(); } BDG_UNLOCK(); return error ; } SYSCTL_PROC(_net_link_ether_bridge, OID_AUTO, enable, CTLTYPE_INT|CTLFLAG_RW, &do_bridge, 0, &sysctl_bdg, "I", "Bridging"); /* * handler for net.link.ether.bridge_cfg */ static int sysctl_bdg_cfg(SYSCTL_HANDLER_ARGS) { int error; char *new_cfg; new_cfg = malloc(sizeof(bridge_cfg), M_TEMP, M_WAITOK); bcopy(bridge_cfg, new_cfg, sizeof(bridge_cfg)); error = sysctl_handle_string(oidp, new_cfg, oidp->oid_arg2, req); if (error == 0) { BDG_LOCK(); if (strcmp(new_cfg, bridge_cfg)) { bcopy(new_cfg, bridge_cfg, sizeof(bridge_cfg)); reconfigure_bridge_locked(); } BDG_UNLOCK(); } free(new_cfg, M_TEMP); return error; } SYSCTL_PROC(_net_link_ether_bridge, OID_AUTO, config, CTLTYPE_STRING|CTLFLAG_RW, &bridge_cfg, sizeof(bridge_cfg), &sysctl_bdg_cfg, "A", "Bridge configuration"); static int sysctl_refresh(SYSCTL_HANDLER_ARGS) { if (req->newptr) reconfigure_bridge(); return 0; } SYSCTL_PROC(_net_link_ether_bridge, OID_AUTO, refresh, CTLTYPE_INT|CTLFLAG_WR, NULL, 0, &sysctl_refresh, "I", "iface refresh"); #ifndef BURN_BRIDGES #define SYSCTL_OID_COMPAT(parent, nbr, name, kind, a1, a2, handler, fmt, descr)\ static struct sysctl_oid sysctl__##parent##_##name##_compat = { \ &sysctl_##parent##_children, { 0 }, \ nbr, kind, a1, a2, #name, handler, fmt, 0, descr }; \ DATA_SET(sysctl_set, sysctl__##parent##_##name##_compat) #define SYSCTL_INT_COMPAT(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID_COMPAT(parent, nbr, name, CTLTYPE_INT|(access), \ ptr, val, sysctl_handle_int, "I", descr) #define SYSCTL_STRUCT_COMPAT(parent, nbr, name, access, ptr, type, descr)\ SYSCTL_OID_COMPAT(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, sizeof(struct type), sysctl_handle_opaque, \ "S," #type, descr) #define SYSCTL_PROC_COMPAT(parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ SYSCTL_OID_COMPAT(parent, nbr, name, (access), \ ptr, arg, handler, fmt, descr) SYSCTL_INT_COMPAT(_net_link_ether, OID_AUTO, bridge_ipf, CTLFLAG_RW, &bdg_ipf, 0,"Pass bridged pkts through IPFilter"); SYSCTL_INT_COMPAT(_net_link_ether, OID_AUTO, bridge_ipfw, CTLFLAG_RW, &bdg_ipfw,0,"Pass bridged pkts through firewall"); SYSCTL_STRUCT_COMPAT(_net_link_ether, PF_BDG, bdgstats, CTLFLAG_RD, &bdg_stats, bdg_stats, "bridge statistics"); SYSCTL_PROC_COMPAT(_net_link_ether, OID_AUTO, bridge_cfg, CTLTYPE_STRING|CTLFLAG_RW, &bridge_cfg, sizeof(bridge_cfg), &sysctl_bdg_cfg, "A", "Bridge configuration"); SYSCTL_PROC_COMPAT(_net_link_ether, OID_AUTO, bridge_refresh, CTLTYPE_INT|CTLFLAG_WR, NULL, 0, &sysctl_refresh, "I", "iface refresh"); #endif static int bdg_loops; static int bdg_slowtimer = 0; static int bdg_age_index = 0; /* index of table position to age */ /* * called periodically to flush entries etc. */ static void bdg_timeout(void *dummy) { if (do_bridge) { int l, i; BDG_LOCK(); /* * age entries in the forwarding table. */ l = bdg_age_index + HASH_SIZE/4 ; if (l > HASH_SIZE) l = HASH_SIZE; for (i = 0; i < n_clusters; i++) { bdg_hash_table *bdg_table = clusters[i].ht; for (; bdg_age_index < l; bdg_age_index++) if (bdg_table[bdg_age_index].used) bdg_table[bdg_age_index].used = 0; else if (bdg_table[bdg_age_index].name) { DPRINTF(("%s: flushing stale entry %d\n", __func__, bdg_age_index)); bdg_table[bdg_age_index].name = NULL; } } if (bdg_age_index >= HASH_SIZE) bdg_age_index = 0; if (--bdg_slowtimer <= 0 ) { bdg_slowtimer = 5; bridge_on(); /* we just need unmute, really */ bdg_loops = 0; } BDG_UNLOCK(); } callout_reset(&bdg_callout, 2*hz, bdg_timeout, NULL); } /* * Find the right pkt destination: * BDG_BCAST is a broadcast * BDG_MCAST is a multicast * BDG_LOCAL is for a local address * BDG_DROP must be dropped * other ifp of the dest. interface (incl.self) * * We assume this is only called for interfaces for which bridging * is enabled, i.e. BDG_USED(ifp) is true. */ static __inline struct ifnet * bridge_dst_lookup(struct ether_header *eh, struct cluster_softc *c) { bdg_hash_table *bt; /* pointer to entry in hash table */ BDG_LOCK_ASSERT(); if (ETHER_IS_MULTICAST(eh->ether_dhost)) return IS_ETHER_BROADCAST(eh->ether_dhost) ? BDG_BCAST : BDG_MCAST; /* * Lookup local addresses in case one matches. We optimize * for the common case of two interfaces. */ KASSERT(c->ports != 0, ("lookup with no ports!")); switch (c->ports) { int i; default: for (i = c->ports-1; i > 1; i--) { if (ETHER_ADDR_EQ(c->my_macs[i].etheraddr, eh->ether_dhost)) return BDG_LOCAL; } /* fall thru... */ case 2: if (ETHER_ADDR_EQ(c->my_macs[1].etheraddr, eh->ether_dhost)) return BDG_LOCAL; case 1: if (ETHER_ADDR_EQ(c->my_macs[0].etheraddr, eh->ether_dhost)) return BDG_LOCAL; } /* * Look for a possible destination in table */ bt = &c->ht[HASH_FN(eh->ether_dhost)]; if (bt->name && ETHER_ADDR_EQ(bt->etheraddr, eh->ether_dhost)) return bt->name; else return BDG_UNKNOWN; } /** * bridge_in() is invoked to perform bridging decision on input packets. * * On Input: * eh Ethernet header of the incoming packet. * ifp interface the packet is coming from. * * 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. */ static struct ifnet * bridge_in(struct ifnet *ifp, struct ether_header *eh) { int index; struct ifnet *dst, *old; bdg_hash_table *bt; /* location in hash table */ int dropit = BDG_MUTED(ifp); /* * hash the source address */ BDG_LOCK(); index = HASH_FN(eh->ether_shost); bt = &BDG_CLUSTER(ifp)->ht[index]; bt->used = 1; old = bt->name; if (old) { /* the entry is valid */ if (!ETHER_ADDR_EQ(eh->ether_shost, bt->etheraddr)) { bdg_ipfw_colls++; bt->name = NULL; /* NB: will overwrite below */ } 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. */ bt->name = ifp; /* relocate address */ printf("-- loop (%d) %6D to %s from %s (%s)\n", bdg_loops, eh->ether_shost, ".", ifp->if_xname, old->if_xname, 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 (bt->name == NULL) { DPRINTF(("%s: new addr %6D at %d for %s\n", __func__, eh->ether_shost, ".", index, ifp->if_xname)); ETHER_ADDR_COPY(bt->etheraddr, eh->ether_shost); bt->name = ifp; } dst = bridge_dst_lookup(eh, BDG_CLUSTER(ifp)); BDG_UNLOCK(); /* * bridge_dst_lookup can return the following values: * BDG_BCAST, BDG_MCAST, BDG_LOCAL, BDG_UNKNOWN, BDG_DROP, ifp. * For muted interfaces, or when we detect a loop, the first 3 are * changed in BDG_LOCAL (we still listen to incoming traffic), * and others to BDG_DROP (no use for the local host). * Also, for incoming packets, ifp is changed to BDG_DROP if ifp == src. * These changes are not necessary for outgoing packets from ether_output(). */ BDG_STAT(ifp, BDG_IN); switch ((uintptr_t)dst) { case (uintptr_t)BDG_BCAST: case (uintptr_t)BDG_MCAST: case (uintptr_t)BDG_LOCAL: case (uintptr_t)BDG_UNKNOWN: case (uintptr_t)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) dst = BDG_LOCAL; else dst = BDG_DROP; } else { if (dst == ifp) dst = BDG_DROP; } DPRINTF(("%s: %6D ->%6D ty 0x%04x dst %s\n", __func__, eh->ether_shost, ".", eh->ether_dhost, ".", ntohs(eh->ether_type), (dst <= BDG_FORWARD) ? bdg_dst_names[(uintptr_t)dst] : dst->if_xname)); return dst; } /* * Return 1 if it's ok to send a packet out the specified interface. * The interface must be: * used for bridging, * not muted, * not full, * up and running, * not the source interface, and * belong to the same cluster as the 'real_dst'. */ static __inline int bridge_ifok(struct ifnet *ifp, struct ifnet *src, struct ifnet *dst) { return (BDG_USED(ifp) && !BDG_MUTED(ifp) && !_IF_QFULL(&ifp->if_snd) && (ifp->if_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) && ifp != src && BDG_SAMECLUSTER(ifp, dst)); } /* * Forward a packet to dst -- which can be a single interface or * an entire cluster. The src port and muted interfaces are excluded. * * If src == NULL, the pkt comes from ether_output, and dst is the real * interface the packet is originally sent to. In this case, we must forward * it to the whole cluster. * We never call bdg_forward from ether_output on interfaces which are * not part of a cluster. * * If possible (i.e. we can determine that the caller does not need * a copy), the packet is consumed here, and bdg_forward returns NULL. * Otherwise, a pointer to a copy of the packet is returned. */ static struct mbuf * bdg_forward(struct mbuf *m0, struct ifnet *dst) { #define EH_RESTORE(_m) do { \ M_PREPEND((_m), ETHER_HDR_LEN, M_DONTWAIT); \ if ((_m) == NULL) { \ bdg_dropped++; \ return NULL; \ } \ if (eh != mtod((_m), struct ether_header *)) \ bcopy(&save_eh, mtod((_m), struct ether_header *), ETHER_HDR_LEN); \ else \ bdg_predict++; \ } while (0); struct ether_header *eh; struct ifnet *src; struct ifnet *ifp, *last; int shared = bdg_copy; /* someone else is using the mbuf */ + int error; struct ifnet *real_dst = dst; /* real dst from ether_output */ struct ip_fw_args args; struct ether_header save_eh; struct mbuf *m; DDB(quad_t ticks; ticks = rdtsc();) args.rule = ip_dn_claim_rule(m0); if (args.rule) shared = 0; /* For sure this is our own mbuf. */ else bdg_thru++; /* count 1st time through bdg_forward */ /* * The packet arrives with the Ethernet header at the front. */ eh = mtod(m0, struct ether_header *); src = m0->m_pkthdr.rcvif; if (src == NULL) { /* packet from ether_output */ BDG_LOCK(); dst = bridge_dst_lookup(eh, BDG_CLUSTER(real_dst)); BDG_UNLOCK(); } if (dst == BDG_DROP) { /* this should not happen */ printf("xx bdg_forward for BDG_DROP\n"); m_freem(m0); bdg_dropped++; 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) { /* need a copy for the local stack */ shared = 1; } /* * Do filtering in a very similar way to what is done in ip_output. * Only if firewall is loaded, enabled, and the packet is not * from ether_output() (src==NULL, or we would filter it twice). * Additional restrictions may apply e.g. non-IP, short packets, * and pkts already gone through a pipe. */ if (src != NULL && ( #ifdef PFIL_HOOKS (inet_pfil_hook.ph_busy_count >= 0 && bdg_ipf != 0) || #endif (IPFW_LOADED && bdg_ipfw != 0))) { int i; if (args.rule != NULL && fw_one_pass) goto forward; /* packet already partially processed */ /* * 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("%s: m_pullup failed\n", __func__); /* XXXDPRINTF*/ bdg_dropped++; return NULL; } eh = mtod(m0, struct ether_header *); } /* * Processing below expects the Ethernet header is stripped. * Furthermore, the mbuf chain might be replaced at various * places. To deal with this we copy the header to a temporary * location, strip the header, and restore it as needed. */ bcopy(eh, &save_eh, ETHER_HDR_LEN); /* local copy for restore */ m_adj(m0, ETHER_HDR_LEN); /* temporarily strip header */ #ifdef PFIL_HOOKS /* * NetBSD-style generic packet filter, pfil(9), hooks. * Enables ipf(8) in bridging. */ if (inet_pfil_hook.ph_busy_count >= 0 && m0->m_pkthdr.len >= sizeof(struct ip) && ntohs(save_eh.ether_type) == ETHERTYPE_IP) { /* * 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 */ struct ip *ip = mtod(m0, struct ip *); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); if (pfil_run_hooks(&inet_pfil_hook, &m0, src, PFIL_IN) != 0) { /* NB: hook should consume packet */ return NULL; } if (m0 == NULL) /* consumed by filter */ return m0; /* * If we get here, the firewall has passed the pkt, but the mbuf * pointer might have changed. Restore ip and the fields ntohs()'d. */ ip = mtod(m0, struct ip *); ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); } #endif /* PFIL_HOOKS */ /* * Prepare arguments and call the firewall. */ if (!IPFW_LOADED || bdg_ipfw == 0) { EH_RESTORE(m0); /* restore Ethernet header */ goto forward; /* not using ipfw, accept the packet */ } /* * XXX The following code is very similar to the one in * if_ethersubr.c:ether_ipfw_chk() */ args.m = m0; /* the packet we are looking at */ args.oif = NULL; /* this is an input packet */ args.next_hop = NULL; /* we do not support forward yet */ args.eh = &save_eh; /* MAC header for bridged/MAC packets */ i = ip_fw_chk_ptr(&args); m0 = args.m; /* in case the firewall used the mbuf */ if (m0 != NULL) EH_RESTORE(m0); /* restore Ethernet header */ if ( (i & IP_FW_PORT_DENY_FLAG) || m0 == NULL) /* drop */ return m0; if (i == 0) /* a PASS rule. */ goto forward; if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG)) { /* * Pass the pkt to dummynet, which consumes it. * If shared, make a copy and keep the original. */ if (shared) { m = m_copypacket(m0, M_DONTWAIT); if (m == NULL) { /* copy failed, give up */ bdg_dropped++; return NULL; } } else { m = m0 ; /* pass the original to dummynet */ m0 = NULL ; /* and nothing back to the caller */ } args.oif = real_dst; ip_dn_io_ptr(m, (i & 0xffff),DN_TO_BDG_FWD, &args); return m0; } /* * XXX at some point, add support for divert/forward actions. * If none of the above matches, we have to drop the packet. */ bdg_ipfw_drops++; return m0; } forward: /* * 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) { bdg_dropped++; return NULL; } /* NB: eh is not used below; no need to recalculate it */ } /* * now real_dst is used to determine the cluster where to forward. * For packets coming from ether_input, this is the one of the 'src' * interface, whereas for locally generated packets (src==NULL) it * is the cluster of the original destination interface, which * was already saved into real_dst. */ if (src != NULL) real_dst = src; last = NULL; if (dst == BDG_BCAST || dst == BDG_MCAST || dst == BDG_UNKNOWN) { /* * Scan all ports and send copies to all but the last. */ IFNET_RLOCK(); /* XXX replace with generation # */ TAILQ_FOREACH(ifp, &ifnet, if_link) { if (bridge_ifok(ifp, src, real_dst)) { if (last) { /* * At this point we know two interfaces need a copy * of the packet (last + ifp) so we must create a * copy to handoff to last. */ m = m_copypacket(m0, M_DONTWAIT); if (m == NULL) { IFNET_RUNLOCK(); printf("%s: , m_copypacket failed!\n", __func__); bdg_dropped++; return m0; /* the original is still there... */ } - if (IF_HANDOFF(&last->if_snd, m, last)) + IFQ_HANDOFF(last, m, error); + if (!error) BDG_STAT(last, BDG_OUT); else bdg_dropped++; } last = ifp; } } IFNET_RUNLOCK(); } else { if (bridge_ifok(dst, src, real_dst)) last = dst; } if (last) { if (shared) { /* need to copy */ m = m_copypacket(m0, M_DONTWAIT); if (m == NULL) { printf("%s: sorry, m_copypacket failed!\n", __func__); bdg_dropped++ ; return m0; /* the original is still there... */ } } else { /* consume original */ m = m0, m0 = NULL; } - if (IF_HANDOFF(&last->if_snd, m, last)) + IFQ_HANDOFF(last, m, error); + if (!error) BDG_STAT(last, BDG_OUT); else bdg_dropped++; } DDB(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; #undef EH_RESTORE } /* * initialization of bridge code. */ static int bdginit(void) { if (bootverbose) printf("BRIDGE %s loaded\n", bridge_version); ifp2sc = malloc(BDG_MAX_PORTS * sizeof(struct bdg_softc), M_IFADDR, M_WAITOK | M_ZERO ); if (ifp2sc == NULL) return ENOMEM; BDG_LOCK_INIT(); n_clusters = 0; clusters = NULL; do_bridge = 0; bzero(&bdg_stats, sizeof(bdg_stats)); bridge_in_ptr = bridge_in; bdg_forward_ptr = bdg_forward; bdgtakeifaces_ptr = reconfigure_bridge; bdgtakeifaces_ptr(); /* XXX does this do anything? */ callout_init(&bdg_callout, CALLOUT_MPSAFE); bdg_timeout(0); return 0 ; } static void bdgdestroy(void) { bridge_in_ptr = NULL; bdg_forward_ptr = NULL; bdgtakeifaces_ptr = NULL; callout_stop(&bdg_callout); BDG_LOCK(); bridge_off(); if (ifp2sc) { free(ifp2sc, M_IFADDR); ifp2sc = NULL; } BDG_LOCK_DESTROY(); } /* * initialization code, both for static and dynamic loading. */ static int bridge_modevent(module_t mod, int type, void *unused) { int err; switch (type) { case MOD_LOAD: if (BDG_LOADED) err = EEXIST; else err = bdginit(); break; case MOD_UNLOAD: do_bridge = 0; bdgdestroy(); err = 0; break; default: err = EINVAL; break; } return err; } static moduledata_t bridge_mod = { "bridge", bridge_modevent, 0 }; DECLARE_MODULE(bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(bridge, 1); Index: head/sys/net/if_arcsubr.c =================================================================== --- head/sys/net/if_arcsubr.c (revision 130548) +++ head/sys/net/if_arcsubr.c (revision 130549) @@ -1,851 +1,848 @@ /* $NetBSD: if_arcsubr.c,v 1.36 2001/06/14 05:44:23 itojun Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1994, 1995 Ignatios Souvatzis * 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: NetBSD: if_ethersubr.c,v 1.9 1994/06/29 06:36:11 cgd Exp * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 * */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #endif #ifdef INET6 #include #endif #ifdef IPX #include #include #endif MODULE_VERSION(arcnet, 1); #define ARCNET_ALLOW_BROKEN_ARP static struct mbuf *arc_defrag(struct ifnet *, struct mbuf *); static int arc_resolvemulti(struct ifnet *, struct sockaddr **, struct sockaddr *); u_int8_t arcbroadcastaddr = 0; #define ARC_LLADDR(ifp) (*(u_int8_t *)IF_LLADDR(ifp)) #define senderr(e) { error = (e); goto bad;} #define SIN(s) ((struct sockaddr_in *)s) #define SIPX(s) ((struct sockaddr_ipx *)s) /* * ARCnet output routine. * Encapsulate a packet of type family for the local net. * Assumes that ifp is actually pointer to arccom structure. */ int arc_output(ifp, m, dst, rt0) struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt0; { struct arc_header *ah; int error; u_int8_t atype, adst; int loop_copy = 0; int isphds; if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) return(ENETDOWN); /* m, m1 aren't initialized yet */ error = 0; switch (dst->sa_family) { #ifdef INET case AF_INET: /* * For now, use the simple IP addr -> ARCnet addr mapping */ if (m->m_flags & (M_BCAST|M_MCAST)) adst = arcbroadcastaddr; /* ARCnet broadcast address */ else if (ifp->if_flags & IFF_NOARP) adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF; else { error = arpresolve(ifp, rt0, m, dst, &adst); if (error) return (error == EWOULDBLOCK ? 0 : error); } atype = (ifp->if_flags & IFF_LINK0) ? ARCTYPE_IP_OLD : ARCTYPE_IP; break; case AF_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_ARCNET); loop_copy = -1; /* if this is for us, don't do it */ switch(ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: atype = ARCTYPE_REVARP; break; case ARPOP_REQUEST: case ARPOP_REPLY: default: atype = ARCTYPE_ARP; break; } if (m->m_flags & M_BCAST) bcopy(ifp->if_broadcastaddr, &adst, ARC_ADDR_LEN); else bcopy(ar_tha(ah), &adst, ARC_ADDR_LEN); } break; #endif #ifdef INET6 case AF_INET6: error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)&adst); if (error) return (error); atype = ARCTYPE_INET6; break; #endif #ifdef IPX case AF_IPX: adst = SIPX(dst)->sipx_addr.x_host.c_host[5]; atype = ARCTYPE_IPX; if (adst == 0xff) adst = arcbroadcastaddr; break; #endif case AF_UNSPEC: loop_copy = -1; ah = (struct arc_header *)dst->sa_data; adst = ah->arc_dhost; atype = ah->arc_type; if (atype == ARCTYPE_ARP) { atype = (ifp->if_flags & IFF_LINK0) ? ARCTYPE_ARP_OLD: ARCTYPE_ARP; #ifdef ARCNET_ALLOW_BROKEN_ARP /* * XXX It's not clear per RFC826 if this is needed, but * "assigned numbers" say this is wrong. * However, e.g., AmiTCP 3.0Beta used it... we make this * switchable for emergency cases. Not perfect, but... */ if (ifp->if_flags & IFF_LINK2) mtod(m, struct arphdr *)->ar_pro = atype - 1; #endif } break; default: if_printf(ifp, "can't handle af%d\n", dst->sa_family); senderr(EAFNOSUPPORT); } isphds = arc_isphds(atype); M_PREPEND(m, isphds ? ARC_HDRNEWLEN : ARC_HDRLEN, M_DONTWAIT); if (m == 0) senderr(ENOBUFS); ah = mtod(m, struct arc_header *); ah->arc_type = atype; ah->arc_dhost = adst; ah->arc_shost = ARC_LLADDR(ifp); if (isphds) { ah->arc_flag = 0; ah->arc_seqid = 0; } if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) { if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); (void) if_simloop(ifp, n, dst->sa_family, ARC_HDRLEN); } else if (ah->arc_dhost == ah->arc_shost) { (void) if_simloop(ifp, m, dst->sa_family, ARC_HDRLEN); return (0); /* XXX */ } } BPF_MTAP(ifp, m); - if (!IF_HANDOFF(&ifp->if_snd, m, ifp)) { - m = 0; - senderr(ENOBUFS); - } + IFQ_HANDOFF(ifp, m, error); return (error); bad: if (m) m_freem(m); return (error); } void arc_frag_init(ifp) struct ifnet *ifp; { struct arccom *ac; ac = (struct arccom *)ifp; ac->curr_frag = 0; } struct mbuf * arc_frag_next(ifp) struct ifnet *ifp; { struct arccom *ac; struct mbuf *m; struct arc_header *ah; ac = (struct arccom *)ifp; if ((m = ac->curr_frag) == 0) { int tfrags; /* dequeue new packet */ IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) return 0; ah = mtod(m, struct arc_header *); if (!arc_isphds(ah->arc_type)) return m; ++ac->ac_seqid; /* make the seqid unique */ tfrags = (m->m_pkthdr.len + ARC_MAX_DATA - 1) / ARC_MAX_DATA; ac->fsflag = 2 * tfrags - 3; ac->sflag = 0; ac->rsflag = ac->fsflag; ac->arc_dhost = ah->arc_dhost; ac->arc_shost = ah->arc_shost; ac->arc_type = ah->arc_type; m_adj(m, ARC_HDRNEWLEN); ac->curr_frag = m; } /* split out next fragment and return it */ if (ac->sflag < ac->fsflag) { /* we CAN'T have short packets here */ ac->curr_frag = m_split(m, ARC_MAX_DATA, M_DONTWAIT); if (ac->curr_frag == 0) { m_freem(m); return 0; } M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); if (m == 0) { m_freem(ac->curr_frag); ac->curr_frag = 0; return 0; } ah = mtod(m, struct arc_header *); ah->arc_flag = ac->rsflag; ah->arc_seqid = ac->ac_seqid; ac->sflag += 2; ac->rsflag = ac->sflag; } else if ((m->m_pkthdr.len >= ARC_MIN_FORBID_LEN - ARC_HDRNEWLEN + 2) && (m->m_pkthdr.len <= ARC_MAX_FORBID_LEN - ARC_HDRNEWLEN + 2)) { ac->curr_frag = 0; M_PREPEND(m, ARC_HDRNEWLEN_EXC, M_DONTWAIT); if (m == 0) return 0; ah = mtod(m, struct arc_header *); ah->arc_flag = 0xFF; ah->arc_seqid = 0xFFFF; ah->arc_type2 = ac->arc_type; ah->arc_flag2 = ac->sflag; ah->arc_seqid2 = ac->ac_seqid; } else { ac->curr_frag = 0; M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); if (m == 0) return 0; ah = mtod(m, struct arc_header *); ah->arc_flag = ac->sflag; ah->arc_seqid = ac->ac_seqid; } ah->arc_dhost = ac->arc_dhost; ah->arc_shost = ac->arc_shost; ah->arc_type = ac->arc_type; return m; } /* * Defragmenter. Returns mbuf if last packet found, else * NULL. frees imcoming mbuf as necessary. */ static __inline struct mbuf * arc_defrag(ifp, m) struct ifnet *ifp; struct mbuf *m; { struct arc_header *ah, *ah1; struct arccom *ac; struct ac_frag *af; struct mbuf *m1; char *s; int newflen; u_char src,dst,typ; ac = (struct arccom *)ifp; if (m->m_len < ARC_HDRNEWLEN) { m = m_pullup(m, ARC_HDRNEWLEN); if (m == NULL) { ++ifp->if_ierrors; return NULL; } } ah = mtod(m, struct arc_header *); typ = ah->arc_type; if (!arc_isphds(typ)) return m; src = ah->arc_shost; dst = ah->arc_dhost; if (ah->arc_flag == 0xff) { m_adj(m, 4); if (m->m_len < ARC_HDRNEWLEN) { m = m_pullup(m, ARC_HDRNEWLEN); if (m == NULL) { ++ifp->if_ierrors; return NULL; } } ah = mtod(m, struct arc_header *); } af = &ac->ac_fragtab[src]; m1 = af->af_packet; s = "debug code error"; if (ah->arc_flag & 1) { /* * first fragment. We always initialize, which is * about the right thing to do, as we only want to * accept one fragmented packet per src at a time. */ if (m1 != NULL) m_freem(m1); af->af_packet = m; m1 = m; af->af_maxflag = ah->arc_flag; af->af_lastseen = 0; af->af_seqid = ah->arc_seqid; return NULL; /* notreached */ } else { /* check for unfragmented packet */ if (ah->arc_flag == 0) return m; /* do we have a first packet from that src? */ if (m1 == NULL) { s = "no first frag"; goto outofseq; } ah1 = mtod(m1, struct arc_header *); if (ah->arc_seqid != ah1->arc_seqid) { s = "seqid differs"; goto outofseq; } if (typ != ah1->arc_type) { s = "type differs"; goto outofseq; } if (dst != ah1->arc_dhost) { s = "dest host differs"; goto outofseq; } /* typ, seqid and dst are ok here. */ if (ah->arc_flag == af->af_lastseen) { m_freem(m); return NULL; } if (ah->arc_flag == af->af_lastseen + 2) { /* ok, this is next fragment */ af->af_lastseen = ah->arc_flag; m_adj(m,ARC_HDRNEWLEN); /* * m_cat might free the first mbuf (with pkthdr) * in 2nd chain; therefore: */ newflen = m->m_pkthdr.len; m_cat(m1,m); m1->m_pkthdr.len += newflen; /* is it the last one? */ if (af->af_lastseen > af->af_maxflag) { af->af_packet = NULL; return(m1); } else return NULL; } s = "other reason"; /* if all else fails, it is out of sequence, too */ } outofseq: if (m1) { m_freem(m1); af->af_packet = NULL; } if (m) m_freem(m); log(LOG_INFO,"%s: got out of seq. packet: %s\n", ifp->if_xname, s); return NULL; } /* * return 1 if Packet Header Definition Standard, else 0. * For now: old IP, old ARP aren't obviously. Lacking correct information, * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS. * (Apple and Novell corporations were involved, among others, in PHDS work). * Easiest is to assume that everybody else uses that, too. */ int arc_isphds(type) u_int8_t type; { return (type != ARCTYPE_IP_OLD && type != ARCTYPE_ARP_OLD && type != ARCTYPE_DIAGNOSE); } /* * Process a received Arcnet packet; * the packet is in the mbuf chain m with * the ARCnet header. */ void arc_input(ifp, m) struct ifnet *ifp; struct mbuf *m; { struct arc_header *ah; int isr; u_int8_t atype; if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } /* possibly defragment: */ m = arc_defrag(ifp, m); if (m == NULL) return; BPF_MTAP(ifp, m); ah = mtod(m, struct arc_header *); /* does this belong to us? */ if ((ifp->if_flags & IFF_PROMISC) == 0 && ah->arc_dhost != arcbroadcastaddr && ah->arc_dhost != ARC_LLADDR(ifp)) { m_freem(m); return; } ifp->if_ibytes += m->m_pkthdr.len; if (ah->arc_dhost == arcbroadcastaddr) { m->m_flags |= M_BCAST|M_MCAST; ifp->if_imcasts++; } atype = ah->arc_type; switch (atype) { #ifdef INET case ARCTYPE_IP: m_adj(m, ARC_HDRNEWLEN); if (ip_fastforward(m)) return; isr = NETISR_IP; break; case ARCTYPE_IP_OLD: m_adj(m, ARC_HDRLEN); if (ip_fastforward(m)) return; isr = NETISR_IP; break; case ARCTYPE_ARP: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } m_adj(m, ARC_HDRNEWLEN); isr = NETISR_ARP; #ifdef ARCNET_ALLOW_BROKEN_ARP mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); #endif break; case ARCTYPE_ARP_OLD: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } m_adj(m, ARC_HDRLEN); isr = NETISR_ARP; #ifdef ARCNET_ALLOW_BROKEN_ARP mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); #endif break; #endif #ifdef INET6 case ARCTYPE_INET6: m_adj(m, ARC_HDRNEWLEN); isr = NETISR_IPV6; break; #endif #ifdef IPX case ARCTYPE_IPX: m_adj(m, ARC_HDRNEWLEN); isr = NETISR_IPX; break; #endif default: m_freem(m); return; } netisr_dispatch(isr, m); } /* * Register (new) link level address. */ void arc_storelladdr(ifp, lla) struct ifnet *ifp; u_int8_t lla; { ARC_LLADDR(ifp) = lla; } /* * Perform common duties while attaching to interface list */ void arc_ifattach(ifp, lla) struct ifnet *ifp; u_int8_t lla; { struct ifaddr *ifa; struct sockaddr_dl *sdl; struct arccom *ac; if_attach(ifp); ifp->if_type = IFT_ARCNET; ifp->if_addrlen = 1; ifp->if_hdrlen = ARC_HDRLEN; ifp->if_mtu = 1500; ifp->if_resolvemulti = arc_resolvemulti; if (ifp->if_baudrate == 0) ifp->if_baudrate = 2500000; #if __FreeBSD_version < 500000 ifa = ifnet_addrs[ifp->if_index - 1]; #else ifa = ifaddr_byindex(ifp->if_index); #endif KASSERT(ifa != NULL, ("%s: no lladdr!\n", __FUNCTION__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ARCNET; sdl->sdl_alen = ifp->if_addrlen; if (ifp->if_flags & IFF_BROADCAST) ifp->if_flags |= IFF_MULTICAST|IFF_ALLMULTI; ac = (struct arccom *)ifp; ac->ac_seqid = (time_second) & 0xFFFF; /* try to make seqid unique */ if (lla == 0) { /* XXX this message isn't entirely clear, to me -- cgd */ log(LOG_ERR,"%s: link address 0 reserved for broadcasts. Please change it and ifconfig %s down up\n", ifp->if_xname, ifp->if_xname); } arc_storelladdr(ifp, lla); ifp->if_broadcastaddr = &arcbroadcastaddr; bpfattach(ifp, DLT_ARCNET, ARC_HDRLEN); } void arc_ifdetach(ifp) struct ifnet *ifp; { bpfdetach(ifp); if_detach(ifp); } int arc_ioctl(ifp, command, data) struct ifnet *ifp; int command; caddr_t data; { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif #ifdef IPX /* * XXX This code is probably wrong */ case AF_IPX: { struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); if (ipx_nullhost(*ina)) ina->x_host.c_host[5] = ARC_LLADDR(ifp); else arc_storelladdr(ifp, ina->x_host.c_host[5]); /* * Set new address */ ifp->if_init(ifp->if_softc); break; } #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) &ifr->ifr_data; *(u_int8_t *)sa->sa_data = ARC_LLADDR(ifp); } break; case SIOCADDMULTI: case SIOCDELMULTI: if (ifr == NULL) error = EAFNOSUPPORT; else { switch (ifr->ifr_addr.sa_family) { case AF_INET: case AF_INET6: error = 0; break; default: error = EAFNOSUPPORT; break; } } break; case SIOCSIFMTU: /* * Set the interface MTU. * mtu can't be larger than ARCMTU for RFC1051 * and can't be larger than ARC_PHDS_MTU */ if (((ifp->if_flags & IFF_LINK0) && ifr->ifr_mtu > ARCMTU) || ifr->ifr_mtu > ARC_PHDS_MAXMTU) error = EINVAL; else ifp->if_mtu = ifr->ifr_mtu; break; } return (error); } /* based on ether_resolvemulti() */ int arc_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 switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; if (*LLADDR(sdl) != arcbroadcastaddr) 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_ZERO); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ARCNET; sdl->sdl_alen = ARC_ADDR_LEN; *LLADDR(sdl) = 0; *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_ZERO); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ARCNET; sdl->sdl_alen = ARC_ADDR_LEN; *LLADDR(sdl) = 0; *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_ef.c =================================================================== --- head/sys/net/if_ef.c (revision 130548) +++ head/sys/net/if_ef.c (revision 130549) @@ -1,594 +1,596 @@ /*- * 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, __func__ ,## args) #else #define EFDEBUG(format, args...) #endif #define EFERROR(format, args...) printf("%s: "format, __func__ ,## args) struct efnet { struct arpcom ef_ac; struct ifnet * ef_ifp; int ef_frametype; }; 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 *ifa2; struct sockaddr_dl *sdl2; 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 */ ifa2 = ifaddr_byindex(sc->ef_ifp->if_index); sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; ether_ifattach(ifp, LLADDR(sdl2)); ifp->if_resolvemulti = 0; ifp->if_type = IFT_XETHER; ifp->if_flags |= IFF_RUNNING; bcopy(LLADDR(sdl2), sc->ef_ac.ac_enaddr, ETHER_ADDR_LEN); EFDEBUG("%s: attached\n", ifp->if_xname); 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; TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { rtinit(ifa, (int)RTM_DELETE, 0); } } } IFNET_WLOCK(); TAILQ_REMOVE(&ifnet, ifp, if_link); IFNET_WUNLOCK(); 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 efnet *sc = ifp->if_softc; struct ifaddr *ifa = (struct ifaddr*)data; int s, error; EFDEBUG("IOCTL %ld for %s\n", cmd, ifp->if_xname); error = 0; s = splimp(); switch (cmd) { case SIOCSIFFLAGS: error = 0; break; case SIOCSIFADDR: if (sc->ef_frametype == ETHER_FT_8023 && ifa->ifa_addr->sa_family != AF_IPX) { error = EAFNOSUPPORT; break; } ifp->if_flags |= IFF_UP; /* FALL THROUGH */ default: error = ether_ioctl(ifp, cmd, data); break; } 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; + int error; ifp->if_flags |= IFF_OACTIVE; p = sc->ef_ifp; EFDEBUG("\n"); for (;;) { IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) break; BPF_MTAP(ifp, m); - if (! IF_HANDOFF(&p->if_snd, m, p)) { + IFQ_HANDOFF(p, m, error); + if (error) { 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, u_short ether_type) { int isr; switch(ether_type) { #ifdef IPX case ETHERTYPE_IPX: isr = NETISR_IPX; break; #endif #ifdef INET case ETHERTYPE_IP: if (ip_fastforward(m)) return (0); isr = NETISR_IP; break; case ETHERTYPE_ARP: isr = NETISR_ARP; break; #endif default: return (EPROTONOSUPPORT); } netisr_dispatch(isr, m); return (0); } static int __inline ef_inputSNAP(struct mbuf *m, struct ether_header *eh, struct llc* l, u_short ether_type) { int isr; switch(ether_type) { #ifdef IPX case ETHERTYPE_IPX: m_adj(m, 8); isr = NETISR_IPX; break; #endif default: return (EPROTONOSUPPORT); } netisr_dispatch(isr, m); return (0); } static int __inline ef_input8022(struct mbuf *m, struct ether_header *eh, struct llc* l, u_short ether_type) { int isr; switch(ether_type) { #ifdef IPX case 0xe0: m_adj(m, 3); isr = NETISR_IPX; break; #endif default: return (EPROTONOSUPPORT); } netisr_dispatch(isr, m); 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 efnet *efp; struct ifnet *eifp; struct llc *l; struct ef_link *efl; int isr; 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; BPF_MTAP2(eifp, eh, ETHER_HDR_LEN, m); /* * Now we ready to adjust mbufs and pass them to protocol intr's */ switch(ft) { case ETHER_FT_EII: return (ef_inputEII(m, eh, ether_type)); #ifdef IPX case ETHER_FT_8023: /* only IPX can be here */ isr = NETISR_IPX; break; #endif case ETHER_FT_SNAP: return (ef_inputSNAP(m, eh, l, ether_type)); case ETHER_FT_8022: return (ef_input8022(m, eh, l, ether_type)); default: EFDEBUG("No support for frame %d and proto %04x\n", ft, ether_type); return (EPROTONOSUPPORT); } netisr_dispatch(isr, m); return (0); } static int ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp, int *hlen) { struct efnet *sc = (struct efnet*)ifp->if_softc; struct mbuf *m = *mp; u_char *cp; short type; if (ifp->if_type != IFT_XETHER) return ENETDOWN; switch (sc->ef_frametype) { 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; efp = (struct efnet*)malloc(sizeof(struct efnet), M_IFADDR, M_WAITOK | M_ZERO); if (efp == NULL) return ENOMEM; efp->ef_ifp = ifp; efp->ef_frametype = ft; eifp = &efp->ef_ac.ac_if; snprintf(eifp->if_xname, IFNAMSIZ, "%sf%d", ifp->if_xname, efp->ef_frametype); eifp->if_dname = "ef"; eifp->if_dunit = IF_DUNIT_NONE; 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; IFNET_RLOCK(); TAILQ_FOREACH(ifp, &ifnet, if_link) { if (ifp->if_type != IFT_ETHER) continue; EFDEBUG("Found interface %s\n", ifp->if_xname); efl = (struct ef_link*)malloc(sizeof(struct ef_link), M_IFADDR, M_WAITOK | M_ZERO); if (efl == NULL) { error = ENOMEM; break; } 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); } IFNET_RUNLOCK(); 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_ethersubr.c =================================================================== --- head/sys/net/if_ethersubr.c (revision 130548) +++ head/sys/net/if_ethersubr.c (revision 130549) @@ -1,1116 +1,1118 @@ /* * 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. * 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_ethersubr.c 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include "opt_bdg.h" #include "opt_mac.h" #include "opt_netgraph.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #include #include #endif #ifdef INET6 #include #endif #ifdef IPX #include #include int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m); int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp, int *hlen); #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 */ /* netgraph node hooks for ng_ether(4) */ void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_attach_p)(struct ifnet *ifp); void (*ng_ether_detach_p)(struct ifnet *ifp); void (*vlan_input_p)(struct ifnet *, struct mbuf *); /* bridge support */ int do_bridge; bridge_in_t *bridge_in_ptr; bdg_forward_t *bdg_forward_ptr; bdgtakeifaces_t *bdgtakeifaces_ptr; struct bdg_softc *ifp2sc; static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ether_resolvemulti(struct ifnet *, struct sockaddr **, struct sockaddr *); #define senderr(e) do { error = (e); goto bad;} while (0) int ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, struct ip_fw **rule, int shared); static int ether_ipfw; /* * Ethernet 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 ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) { short type; int error, hdrcmplt = 0; u_char esrc[ETHER_ADDR_LEN], edst[ETHER_ADDR_LEN]; struct ether_header *eh; int loop_copy = 0; int hlen; /* link layer header length */ #ifdef MAC error = mac_check_ifnet_transmit(ifp, m); if (error) senderr(error); #endif if (ifp->if_flags & IFF_MONITOR) senderr(ENETDOWN); if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); hlen = ETHER_HDR_LEN; switch (dst->sa_family) { #ifdef INET case AF_INET: error = arpresolve(ifp, rt0, m, dst, edst); if (error) return (error == EWOULDBLOCK ? 0 : error); type = htons(ETHERTYPE_IP); break; case AF_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_ETHER); loop_copy = -1; /* if this is for us, don't do it */ switch(ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: type = htons(ETHERTYPE_REVARP); break; case ARPOP_REQUEST: case ARPOP_REPLY: default: type = htons(ETHERTYPE_ARP); break; } if (m->m_flags & M_BCAST) bcopy(ifp->if_broadcastaddr, edst, ETHER_ADDR_LEN); else bcopy(ar_tha(ah), edst, ETHER_ADDR_LEN); } break; #endif #ifdef INET6 case AF_INET6: error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst); if (error) return error; type = htons(ETHERTYPE_IPV6); break; #endif #ifdef IPX case AF_IPX: if (ef_outputp) { error = ef_outputp(ifp, &m, dst, &type, &hlen); if (error) goto bad; } else 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 ((aa = at_ifawithnet((struct sockaddr_at *)dst)) == NULL) senderr(EHOSTUNREACH); /* XXX */ if (!aarpresolve(ifp, m, (struct sockaddr_at *)dst, edst)) return (0); /* * In the phase 2 case, need to prepend an mbuf for the llc header. */ if ( aa->aa_flags & AFA_PHASE2 ) { struct llc llc; M_PREPEND(m, LLC_SNAPFRAMELEN, M_TRYWAIT); if (m == NULL) 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), LLC_SNAPFRAMELEN); type = htons(m->m_pkthdr.len); hlen = LLC_SNAPFRAMELEN + ETHER_HDR_LEN; } else { type = htons(ETHERTYPE_AT); } break; } #endif /* NETATALK */ case pseudo_AF_HDRCMPLT: hdrcmplt = 1; eh = (struct ether_header *)dst->sa_data; (void)memcpy(esrc, eh->ether_shost, sizeof (esrc)); /* FALLTHROUGH */ case AF_UNSPEC: loop_copy = -1; /* if this is for us, don't do it */ eh = (struct ether_header *)dst->sa_data; (void)memcpy(edst, eh->ether_dhost, sizeof (edst)); type = eh->ether_type; break; default: if_printf(ifp, "can't handle af%d\n", dst->sa_family); senderr(EAFNOSUPPORT); } /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (m == NULL) senderr(ENOBUFS); eh = mtod(m, struct ether_header *); (void)memcpy(&eh->ether_type, &type, sizeof(eh->ether_type)); (void)memcpy(eh->ether_dhost, edst, sizeof (edst)); if (hdrcmplt) (void)memcpy(eh->ether_shost, esrc, sizeof(eh->ether_shost)); else (void)memcpy(eh->ether_shost, IFP2AC(ifp)->ac_enaddr, sizeof(eh->ether_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)) { int csum_flags = 0; if (m->m_pkthdr.csum_flags & CSUM_IP) csum_flags |= (CSUM_IP_CHECKED|CSUM_IP_VALID); if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) csum_flags |= (CSUM_DATA_VALID|CSUM_PSEUDO_HDR); if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { struct mbuf *n; if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { n->m_pkthdr.csum_flags |= csum_flags; if (csum_flags & CSUM_DATA_VALID) n->m_pkthdr.csum_data = 0xffff; (void)if_simloop(ifp, n, dst->sa_family, hlen); } else ifp->if_iqdrops++; } else if (bcmp(eh->ether_dhost, eh->ether_shost, ETHER_ADDR_LEN) == 0) { m->m_pkthdr.csum_flags |= csum_flags; if (csum_flags & CSUM_DATA_VALID) m->m_pkthdr.csum_data = 0xffff; (void) if_simloop(ifp, m, dst->sa_family, hlen); return (0); /* XXX */ } } /* Handle ng_ether(4) processing, if any */ if (ng_ether_output_p != NULL) { if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) { bad: if (m != NULL) m_freem(m); return (error); } if (m == NULL) return (0); } /* Continue with link-layer output */ return ether_output_frame(ifp, m); } /* * Ethernet link layer output routine to send a raw frame to the device. * * This assumes that the 14 byte Ethernet header is present and contiguous * in the first mbuf (if BRIDGE'ing). */ int ether_output_frame(struct ifnet *ifp, struct mbuf *m) { struct ip_fw *rule = ip_dn_claim_rule(m); + int error; if (rule == NULL && BDG_ACTIVE(ifp)) { /* * Beware, the bridge code notices the null rcvif and * uses that identify that it's being called from * ether_output as opposd to ether_input. Yech. */ m->m_pkthdr.rcvif = NULL; m = bdg_forward_ptr(m, ifp); if (m != NULL) m_freem(m); return (0); } if (IPFW_LOADED && ether_ipfw != 0) { if (ether_ipfw_chk(&m, ifp, &rule, 0) == 0) { if (m) { m_freem(m); return EACCES; /* pkt dropped */ } else return 0; /* consumed e.g. in a pipe */ } } /* * Queue message on interface, update output statistics if * successful, and start output if interface not yet active. */ - return (IF_HANDOFF(&ifp->if_snd, m, ifp) ? 0 : ENOBUFS); + IFQ_HANDOFF(ifp, m, error); + return (error); } /* * ipfw processing for ethernet packets (in and out). * The second parameter is NULL from ether_demux, and ifp from * ether_output_frame. This section of code could be used from * bridge.c as well as long as we use some extra info * to distinguish that case from ether_output_frame(); */ int ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, struct ip_fw **rule, int shared) { struct ether_header *eh; struct ether_header save_eh; struct mbuf *m; int i; struct ip_fw_args args; if (*rule != NULL && fw_one_pass) return 1; /* dummynet packet, already partially processed */ /* * 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. */ m = *m0; i = min( m->m_pkthdr.len, max_protohdr); if ( shared || m->m_len < i) { m = m_pullup(m, i); if (m == NULL) { *m0 = m; return 0; } } eh = mtod(m, struct ether_header *); save_eh = *eh; /* save copy for restore below */ m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ args.m = m; /* the packet we are looking at */ args.oif = dst; /* destination, if any */ args.rule = *rule; /* matching rule to restart */ args.next_hop = NULL; /* we do not support forward yet */ args.eh = &save_eh; /* MAC header for bridged/MAC packets */ i = ip_fw_chk_ptr(&args); m = args.m; if (m != NULL) { /* * Restore Ethernet header, as needed, in case the * mbuf chain was replaced by ipfw. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (m == NULL) { *m0 = m; return 0; } if (eh != mtod(m, struct ether_header *)) bcopy(&save_eh, mtod(m, struct ether_header *), ETHER_HDR_LEN); } *m0 = m; *rule = args.rule; if ( (i & IP_FW_PORT_DENY_FLAG) || m == NULL) /* drop */ return 0; if (i == 0) /* a PASS rule. */ return 1; if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG)) { /* * Pass the pkt to dummynet, which consumes it. * If shared, make a copy and keep the original. */ if (shared) { m = m_copypacket(m, M_DONTWAIT); if (m == NULL) return 0; } else { /* * Pass the original to dummynet and * nothing back to the caller */ *m0 = NULL ; } ip_dn_io_ptr(m, (i & 0xffff), dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args); return 0; } /* * XXX at some point add support for divert/forward actions. * If none of the above matches, we have to drop the pkt. */ return 0; } /* * Process a received Ethernet packet; the packet is in the * mbuf chain m with the ethernet header at the front. */ static void ether_input(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; u_short etype; /* * Do consistency checks to verify assumptions * made by code past this point. */ if ((m->m_flags & M_PKTHDR) == 0) { if_printf(ifp, "discard frame w/o packet header\n"); ifp->if_ierrors++; m_freem(m); return; } if (m->m_len < ETHER_HDR_LEN) { /* XXX maybe should pullup? */ if_printf(ifp, "discard frame w/o leading ethernet " "header (len %u pkt len %u)\n", m->m_len, m->m_pkthdr.len); ifp->if_ierrors++; m_freem(m); return; } eh = mtod(m, struct ether_header *); etype = ntohs(eh->ether_type); if (m->m_pkthdr.len > ETHER_MAX_FRAME(ifp, etype, m->m_flags & M_HASFCS)) { if_printf(ifp, "discard oversize frame " "(ether type %x flags %x len %u > max %lu)\n", etype, m->m_flags, m->m_pkthdr.len, ETHER_MAX_FRAME(ifp, etype, m->m_flags & M_HASFCS)); ifp->if_ierrors++; m_freem(m); return; } if (m->m_pkthdr.rcvif == NULL) { if_printf(ifp, "discard frame w/o interface pointer\n"); ifp->if_ierrors++; m_freem(m); return; } #ifdef DIAGNOSTIC if (m->m_pkthdr.rcvif != ifp) { if_printf(ifp, "Warning, frame marked as received on %s\n", m->m_pkthdr.rcvif->if_xname); } #endif #ifdef MAC /* * Tag the mbuf with an appropriate MAC label before any other * consumers can get to it. */ mac_create_mbuf_from_ifnet(ifp, m); #endif /* * Give bpf a chance at the packet. */ BPF_MTAP(ifp, m); if (ifp->if_flags & IFF_MONITOR) { /* * Interface marked for monitoring; discard packet. */ m_freem(m); return; } /* If the CRC is still on the packet, trim it off. */ if (m->m_flags & M_HASFCS) { m_adj(m, -ETHER_CRC_LEN); m->m_flags &= ~M_HASFCS; } ifp->if_ibytes += m->m_pkthdr.len; /* Handle ng_ether(4) processing, if any */ if (ng_ether_input_p != NULL) { (*ng_ether_input_p)(ifp, &m); if (m == NULL) return; } /* Check for bridging mode */ if (BDG_ACTIVE(ifp) ) { struct ifnet *bif; /* * Check with bridging code to see how the packet * should be handled. Possibilities are: * * BDG_BCAST broadcast * BDG_MCAST multicast * BDG_LOCAL for local address, don't forward * BDG_DROP discard * ifp forward only to specified interface(s) * * Non-local destinations are handled by passing the * packet back to the bridge code. */ bif = bridge_in_ptr(ifp, eh); if (bif == BDG_DROP) { /* discard packet */ m_freem(m); return; } if (bif != BDG_LOCAL) { /* non-local, forward */ m = bdg_forward_ptr(m, bif); /* * The bridge may consume the packet if it's not * supposed to be passed up or if a problem occurred * while doing its job. This is reflected by it * returning a NULL mbuf pointer. */ if (m == NULL) { if (bif == BDG_BCAST || bif == BDG_MCAST) if_printf(ifp, "bridge dropped %s packet\n", bif == BDG_BCAST ? "broadcast" : "multicast"); return; } /* * But in some cases the bridge may return the * packet for us to free; sigh. */ if (bif != BDG_BCAST && bif != BDG_MCAST) { m_freem(m); return; } } } ether_demux(ifp, m); /* First chunk of an mbuf contains good entropy */ if (harvest.ethernet) random_harvest(m, 16, 3, 0, RANDOM_NET); } /* * Upper layer processing for a received Ethernet packet. */ void ether_demux(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; int isr; u_short ether_type; #if defined(NETATALK) struct llc *l; #endif struct ip_fw *rule = ip_dn_claim_rule(m); KASSERT(ifp != NULL, ("ether_demux: NULL interface pointer")); eh = mtod(m, struct ether_header *); if (rule) /* packet was already bridged */ goto post_stats; if (!(BDG_ACTIVE(ifp))) { /* * Discard packet if upper layers shouldn't see it because it * was unicast to a different Ethernet address. If the driver * is working properly, then this situation can only happen * when the interface is in promiscuous mode. */ if ((ifp->if_flags & IFF_PROMISC) != 0 && (eh->ether_dhost[0] & 1) == 0 && bcmp(eh->ether_dhost, IFP2AC(ifp)->ac_enaddr, ETHER_ADDR_LEN) != 0 && (ifp->if_flags & IFF_PPROMISC) == 0) { m_freem(m); return; } } /* Discard packet if interface is not up */ if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (bcmp(etherbroadcastaddr, eh->ether_dhost, sizeof(etherbroadcastaddr)) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; } if (m->m_flags & (M_BCAST|M_MCAST)) ifp->if_imcasts++; post_stats: if (IPFW_LOADED && ether_ipfw != 0) { if (ether_ipfw_chk(&m, NULL, &rule, 0) == 0) { if (m) m_freem(m); return; } } /* * If VLANs are configured on the interface, check to * see if the device performed the decapsulation and * provided us with the tag. */ if (ifp->if_nvlans && m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL) != NULL) { /* * vlan_input() will either recursively call ether_input() * or drop the packet. */ KASSERT(vlan_input_p != NULL,("ether_input: VLAN not loaded!")); (*vlan_input_p)(ifp, m); return; } ether_type = ntohs(eh->ether_type); /* * Handle protocols that expect to have the Ethernet header * (and possibly FCS) intact. */ switch (ether_type) { case ETHERTYPE_VLAN: if (ifp->if_nvlans != 0) { KASSERT(vlan_input_p,("ether_input: VLAN not loaded!")); (*vlan_input_p)(ifp, m); } else { ifp->if_noproto++; m_freem(m); } return; } /* Strip off Ethernet header. */ m_adj(m, ETHER_HDR_LEN); /* If the CRC is still on the packet, trim it off. */ if (m->m_flags & M_HASFCS) { m_adj(m, -ETHER_CRC_LEN); m->m_flags &= ~M_HASFCS; } switch (ether_type) { #ifdef INET case ETHERTYPE_IP: if (ip_fastforward(m)) return; isr = NETISR_IP; break; case ETHERTYPE_ARP: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } isr = NETISR_ARP; break; #endif #ifdef IPX case ETHERTYPE_IPX: if (ef_inputp && ef_inputp(ifp, eh, m) == 0) return; isr = NETISR_IPX; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; break; #endif #ifdef NETATALK case ETHERTYPE_AT: isr = NETISR_ATALK1; break; case ETHERTYPE_AARP: isr = NETISR_AARP; break; #endif /* NETATALK */ default: #ifdef IPX if (ef_inputp && ef_inputp(ifp, eh, m) == 0) return; #endif /* IPX */ #if defined(NETATALK) if (ether_type > ETHERMTU) goto discard; l = mtod(m, struct llc *); if (l->llc_dsap == LLC_SNAP_LSAP && l->llc_ssap == LLC_SNAP_LSAP && l->llc_control == LLC_UI) { if (bcmp(&(l->llc_snap_org_code)[0], at_org_code, sizeof(at_org_code)) == 0 && ntohs(l->llc_snap_ether_type) == ETHERTYPE_AT) { m_adj(m, LLC_SNAPFRAMELEN); isr = NETISR_ATALK2; 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, LLC_SNAPFRAMELEN); isr = NETISR_AARP; break; } } #endif /* NETATALK */ goto discard; } netisr_dispatch(isr, m); return; discard: /* * Packet is to be discarded. If netgraph is present, * hand the packet to it for last chance processing; * otherwise dispose of it. */ if (ng_ether_input_orphan_p != NULL) { /* * Put back the ethernet header so netgraph has a * consistent view of inbound packets. */ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); (*ng_ether_input_orphan_p)(ifp, m); return; } m_freem(m); } /* * Convert Ethernet address to printable (loggable) representation. * This routine is for compatibility; it's better to just use * * printf("%6D", , ":"); * * since there's no static buffer involved. */ char * ether_sprintf(const u_char *ap) { static char etherbuf[18]; snprintf(etherbuf, sizeof (etherbuf), "%6D", ap, ":"); return (etherbuf); } /* * Perform common duties while attaching to interface list */ void ether_ifattach(struct ifnet *ifp, const u_int8_t *llc) { struct ifaddr *ifa; struct sockaddr_dl *sdl; ifp->if_type = IFT_ETHER; ifp->if_addrlen = ETHER_ADDR_LEN; ifp->if_hdrlen = ETHER_HDR_LEN; if_attach(ifp); ifp->if_mtu = ETHERMTU; ifp->if_output = ether_output; ifp->if_input = ether_input; ifp->if_resolvemulti = ether_resolvemulti; if (ifp->if_baudrate == 0) ifp->if_baudrate = IF_Mbps(10); /* just a default */ ifp->if_broadcastaddr = etherbroadcastaddr; ifa = ifaddr_byindex(ifp->if_index); KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ifp->if_addrlen; bcopy(llc, LLADDR(sdl), ifp->if_addrlen); /* * XXX: This doesn't belong here; we do it until * XXX: all drivers are cleaned up */ if (llc != IFP2AC(ifp)->ac_enaddr) bcopy(llc, IFP2AC(ifp)->ac_enaddr, ifp->if_addrlen); bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN); if (ng_ether_attach_p != NULL) (*ng_ether_attach_p)(ifp); if (BDG_LOADED) bdgtakeifaces_ptr(); /* Announce Ethernet MAC address. */ if_printf(ifp, "Ethernet address: %6D\n", llc, ":"); } /* * Perform common duties while detaching an Ethernet interface */ void ether_ifdetach(struct ifnet *ifp) { if (ng_ether_detach_p != NULL) (*ng_ether_detach_p)(ifp); bpfdetach(ifp); if_detach(ifp); if (BDG_LOADED) bdgtakeifaces_ptr(); } SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet"); SYSCTL_INT(_net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW, ðer_ipfw,0,"Pass ether pkts through firewall"); #if 0 /* * This is for reference. We have a table-driven version * of the little-endian crc32 generator, which is faster * than the double-loop. */ uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { size_t i; uint32_t crc; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) carry = (crc ^ data) & 1; crc >>= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_LE); } return (crc); } #else uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { static const uint32_t crctab[] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; size_t i; uint32_t crc; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { crc ^= buf[i]; crc = (crc >> 4) ^ crctab[crc & 0xf]; crc = (crc >> 4) ^ crctab[crc & 0xf]; } return (crc); } #endif uint32_t ether_crc32_be(const uint8_t *buf, size_t len) { size_t i; uint32_t crc, carry; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01); crc <<= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_BE) | carry; } } return (crc); } int ether_ioctl(struct ifnet *ifp, int command, caddr_t data) { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif #ifdef IPX /* * XXX - This code is probably wrong */ case AF_IPX: { struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); struct arpcom *ac = IFP2AC(ifp); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) ac->ac_enaddr; else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) ac->ac_enaddr, sizeof(ac->ac_enaddr)); } /* * Set new address */ ifp->if_init(ifp->if_softc); break; } #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; default: error = EINVAL; /* XXX netbsd has ENOTTY??? */ break; } return (error); } static int ether_resolvemulti(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|M_ZERO); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; 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|M_ZERO); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; 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; } } static moduledata_t ether_mod = { "ether", NULL, 0 }; DECLARE_MODULE(ether, ether_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(ether, 1); Index: head/sys/net/if_fddisubr.c =================================================================== --- head/sys/net/if_fddisubr.c (revision 130548) +++ head/sys/net/if_fddisubr.c (revision 130549) @@ -1,782 +1,784 @@ /* * 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 "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #endif #ifdef INET6 #include #endif #ifdef IPX #include #include #endif #ifdef DECNET #include #endif #ifdef NETATALK #include #include #include extern u_char at_org_code[ 3 ]; extern u_char aarp_org_code[ 3 ]; #endif /* NETATALK */ static const u_char fddibroadcastaddr[FDDI_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int fddi_resolvemulti(struct ifnet *, struct sockaddr **, struct sockaddr *); static int fddi_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); static void fddi_input(struct ifnet *ifp, struct mbuf *m); #define senderr(e) do { error = (e); goto bad; } while (0) /* * 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. */ static int fddi_output(ifp, m, dst, rt0) 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[FDDI_ADDR_LEN], edst[FDDI_ADDR_LEN]; struct fddi_header *fh; #ifdef MAC error = mac_check_ifnet_transmit(ifp, m); if (error) senderr(error); #endif if (ifp->if_flags & IFF_MONITOR) senderr(ENETDOWN); if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); getmicrotime(&ifp->if_lastchange); switch (dst->sa_family) { #ifdef INET case AF_INET: { error = arpresolve(ifp, rt0, m, dst, edst); if (error) return (error == EWOULDBLOCK ? 0 : error); type = htons(ETHERTYPE_IP); break; } case AF_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_ETHER); loop_copy = -1; /* if this is for us, don't do it */ switch (ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: type = htons(ETHERTYPE_REVARP); break; case ARPOP_REQUEST: case ARPOP_REPLY: default: type = htons(ETHERTYPE_ARP); break; } if (m->m_flags & M_BCAST) bcopy(ifp->if_broadcastaddr, edst, FDDI_ADDR_LEN); else bcopy(ar_tha(ah), edst, FDDI_ADDR_LEN); } break; #endif /* INET */ #ifdef INET6 case AF_INET6: error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst); if (error) return (error); /* Something bad happened */ type = htons(ETHERTYPE_IPV6); break; #endif /* INET6 */ #ifdef IPX case AF_IPX: type = htons(ETHERTYPE_IPX); bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host), (caddr_t)edst, FDDI_ADDR_LEN); break; #endif /* IPX */ #ifdef NETATALK case AF_APPLETALK: { struct at_ifaddr *aa; if (!aarpresolve(ifp, 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, LLC_SNAPFRAMELEN, 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), LLC_SNAPFRAMELEN); type = 0; } else { type = htons(ETHERTYPE_AT); } break; } #endif /* NETATALK */ case pseudo_AF_HDRCMPLT: { struct ether_header *eh; hdrcmplt = 1; eh = (struct ether_header *)dst->sa_data; bcopy((caddr_t)eh->ether_shost, (caddr_t)esrc, FDDI_ADDR_LEN); /* FALLTHROUGH */ } case AF_UNSPEC: { struct ether_header *eh; loop_copy = -1; eh = (struct ether_header *)dst->sa_data; bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, FDDI_ADDR_LEN); 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: if_printf(ifp, "can't handle af%d\n", dst->sa_family); senderr(EAFNOSUPPORT); } /* * Add LLC header. */ if (type != 0) { struct llc *l; M_PREPEND(m, LLC_SNAPFRAMELEN, 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; l->llc_snap.ether_type = htons(type); } /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, FDDI_HDR_LEN, M_DONTWAIT); if (m == 0) senderr(ENOBUFS); fh = mtod(m, struct fddi_header *); fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4; bcopy((caddr_t)edst, (caddr_t)fh->fddi_dhost, FDDI_ADDR_LEN); queue_it: if (hdrcmplt) bcopy((caddr_t)esrc, (caddr_t)fh->fddi_shost, FDDI_ADDR_LEN); else bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t)fh->fddi_shost, FDDI_ADDR_LEN); /* * 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 > 0)) { struct mbuf *n; n = m_copy(m, 0, (int)M_COPYALL); (void) if_simloop(ifp, n, dst->sa_family, FDDI_HDR_LEN); } else if (bcmp(fh->fddi_dhost, fh->fddi_shost, FDDI_ADDR_LEN) == 0) { (void) if_simloop(ifp, m, dst->sa_family, FDDI_HDR_LEN); return (0); /* XXX */ } } - if (! IF_HANDOFF(&ifp->if_snd, m, ifp)) - senderr(ENOBUFS); + IFQ_HANDOFF(ifp, m, error); + if (error) + ifp->if_oerrors++; + return (error); bad: ifp->if_oerrors++; if (m) m_freem(m); return (error); } /* * Process a received FDDI packet. */ static void fddi_input(ifp, m) struct ifnet *ifp; struct mbuf *m; { int isr; struct llc *l; struct fddi_header *fh; /* * Do consistency checks to verify assumptions * made by code past this point. */ if ((m->m_flags & M_PKTHDR) == 0) { if_printf(ifp, "discard frame w/o packet header\n"); ifp->if_ierrors++; m_freem(m); return; } if (m->m_pkthdr.rcvif == NULL) { if_printf(ifp, "discard frame w/o interface pointer\n"); ifp->if_ierrors++; m_freem(m); return; } m = m_pullup(m, FDDI_HDR_LEN); if (m == NULL) { ifp->if_ierrors++; goto dropanyway; } fh = mtod(m, struct fddi_header *); m->m_pkthdr.header = (void *)fh; /* * Discard packet if interface is not up. */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) goto dropanyway; /* * Give bpf a chance at the packet. */ BPF_MTAP(ifp, m); /* * Interface marked for monitoring; discard packet. */ if (ifp->if_flags & IFF_MONITOR) { m_freem(m); return; } #ifdef MAC mac_create_mbuf_from_ifnet(ifp, m); #endif /* * Update interface statistics. */ ifp->if_ibytes += m->m_pkthdr.len; getmicrotime(&ifp->if_lastchange); /* * Discard non local unicast packets when interface * is in promiscuous mode. */ if ((ifp->if_flags & IFF_PROMISC) && ((fh->fddi_dhost[0] & 1) == 0) && (bcmp(IFP2AC(ifp)->ac_enaddr, (caddr_t)fh->fddi_dhost, FDDI_ADDR_LEN) != 0)) goto dropanyway; /* * Set mbuf flags for bcast/mcast. */ if (fh->fddi_dhost[0] & 1) { if (bcmp(ifp->if_broadcastaddr, fh->fddi_dhost, FDDI_ADDR_LEN) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; ifp->if_imcasts++; } #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 /* Strip off FDDI header. */ m_adj(m, FDDI_HDR_LEN); m = m_pullup(m, LLC_SNAPFRAMELEN); if (m == 0) { ifp->if_ierrors++; goto dropanyway; } l = mtod(m, struct llc *); switch (l->llc_dsap) { case LLC_SNAP_LSAP: { u_int16_t type; if ((l->llc_control != LLC_UI) || (l->llc_ssap != LLC_SNAP_LSAP)) { ifp->if_noproto++; 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) { isr = NETISR_ATALK2; m_adj(m, LLC_SNAPFRAMELEN); 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, LLC_SNAPFRAMELEN); isr = NETISR_AARP; break; } #endif /* NETATALK */ if (l->llc_snap.org_code[0] != 0 || l->llc_snap.org_code[1] != 0 || l->llc_snap.org_code[2] != 0) { ifp->if_noproto++; goto dropanyway; } type = ntohs(l->llc_snap.ether_type); m_adj(m, LLC_SNAPFRAMELEN); switch (type) { #ifdef INET case ETHERTYPE_IP: if (ip_fastforward(m)) return; isr = NETISR_IP; break; case ETHERTYPE_ARP: if (ifp->if_flags & IFF_NOARP) goto dropanyway; isr = NETISR_ARP; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; break; #endif #ifdef IPX case ETHERTYPE_IPX: isr = NETISR_IPX; break; #endif #ifdef DECNET case ETHERTYPE_DECNET: isr = NETISR_DECNET; break; #endif #ifdef NETATALK case ETHERTYPE_AT: isr = NETISR_ATALK1; break; case ETHERTYPE_AARP: isr = NETISR_AARP; break; #endif /* NETATALK */ default: /* printf("fddi_input: unknown protocol 0x%x\n", type); */ ifp->if_noproto++; goto dropanyway; } break; } default: /* printf("fddi_input: unknown dsap 0x%x\n", l->llc_dsap); */ ifp->if_noproto++; goto dropanyway; } netisr_dispatch(isr, m); return; dropanyway: ifp->if_iqdrops++; if (m) m_freem(m); return; } /* * Perform common duties while attaching to interface list */ void fddi_ifattach(ifp, bpf) struct ifnet *ifp; int bpf; { struct ifaddr *ifa; struct sockaddr_dl *sdl; ifp->if_type = IFT_FDDI; ifp->if_addrlen = FDDI_ADDR_LEN; ifp->if_hdrlen = 21; if_attach(ifp); /* Must be called before additional assignments */ ifp->if_mtu = FDDIMTU; ifp->if_output = fddi_output; ifp->if_input = fddi_input; ifp->if_resolvemulti = fddi_resolvemulti; ifp->if_broadcastaddr = fddibroadcastaddr; ifp->if_baudrate = 100000000; #ifdef IFF_NOTRAILERS ifp->if_flags |= IFF_NOTRAILERS; #endif ifa = ifaddr_byindex(ifp->if_index); if (ifa == NULL) { if_printf(ifp, "%s() no lladdr!\n", __func__); return; } sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_FDDI; sdl->sdl_alen = ifp->if_addrlen; bcopy(IFP2AC(ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); if (bpf) bpfattach(ifp, DLT_FDDI, FDDI_HDR_LEN); return; } void fddi_ifdetach(ifp, bpf) struct ifnet *ifp; int bpf; { if (bpf) bpfdetach(ifp); if_detach(ifp); return; } int fddi_ioctl (ifp, command, data) struct ifnet *ifp; int command; caddr_t data; { struct ifaddr *ifa; struct ifreq *ifr; int error; ifa = (struct ifaddr *) data; ifr = (struct ifreq *) data; error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: /* before arpwhohas */ ifp->if_init(ifp->if_softc); arp_ifinit(ifp, ifa); break; #endif #ifdef IPX /* * XXX - This code is probably wrong */ case AF_IPX: { struct ipx_addr *ina; struct arpcom *ac; ina = &(IA_SIPX(ifa)->sipx_addr); ac = IFP2AC(ifp); if (ipx_nullhost(*ina)) { ina->x_host = *(union ipx_host *) ac->ac_enaddr; } else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) ac->ac_enaddr, sizeof(ac->ac_enaddr)); } /* * Set new address */ ifp->if_init(ifp->if_softc); } break; #endif default: ifp->if_init(ifp->if_softc); break; } case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t) sa->sa_data, FDDI_ADDR_LEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > FDDIMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; default: break; } return (error); } 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 = FDDI_ADDR_LEN; 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 = FDDI_ADDR_LEN; 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); } return (0); } static moduledata_t fddi_mod = { "fddi", /* module name */ NULL, /* event handler */ 0 /* extra data */ }; DECLARE_MODULE(fddi, fddi_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(fddi, 1); Index: head/sys/net/if_fwsubr.c =================================================================== --- head/sys/net/if_fwsubr.c (revision 130548) +++ head/sys/net/if_fwsubr.c (revision 130549) @@ -1,801 +1,803 @@ /* * Copyright (c) 2004 Doug Rabson * 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. * 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. * * $FreeBSD$ */ #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #include #include #endif #ifdef INET6 #include #endif #define IFP2FC(IFP) ((struct fw_com *)IFP) struct fw_hwaddr firewire_broadcastaddr = { 0xffffffff, 0xffffffff, 0xff, 0xff, 0xffff, 0xffffffff }; static int firewire_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) { struct fw_com *fc = (struct fw_com *) ifp; int error, type; struct rtentry *rt; struct m_tag *mtag; union fw_encap *enc; struct fw_hwaddr *destfw; uint8_t speed; uint16_t psize, fsize, dsize; struct mbuf *mtail; int unicast, dgl, foff; static int next_dgl; GIANT_REQUIRED; #ifdef MAC error = mac_check_ifnet_transmit(ifp, m); if (error) goto bad; #endif if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { error = ENETDOWN; goto bad; } error = rt_check(&rt, &rt0, dst); if (error) goto bad; /* * For unicast, we make a tag to store the lladdr of the * destination. This might not be the first time we have seen * the packet (for instance, the arp code might be trying to * re-send it after receiving an arp reply) so we only * allocate a tag if there isn't one there already. For * multicast, we will eventually use a different tag to store * the channel number. */ unicast = !(m->m_flags & (M_BCAST | M_MCAST)); if (unicast) { mtag = m_tag_locate(m, MTAG_FIREWIRE, MTAG_FIREWIRE_HWADDR, NULL); if (!mtag) { mtag = m_tag_alloc(MTAG_FIREWIRE, MTAG_FIREWIRE_HWADDR, sizeof (struct fw_hwaddr), M_NOWAIT); if (!mtag) { error = ENOMEM; goto bad; } m_tag_prepend(m, mtag); } destfw = (struct fw_hwaddr *)(mtag + 1); } else { destfw = 0; } switch (dst->sa_family) { #ifdef AF_INET case AF_INET: /* * Only bother with arp for unicast. Allocation of * channels etc. for firewire is quite different and * doesn't fit into the arp model. */ if (unicast) { error = arpresolve(ifp, rt, m, dst, (u_char *) destfw); if (error) return (error == EWOULDBLOCK ? 0 : error); } type = ETHERTYPE_IP; break; case AF_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_IEEE1394); type = ETHERTYPE_ARP; if (unicast) *destfw = *(struct fw_hwaddr *) ar_tha(ah); /* * The standard arp code leaves a hole for the target * hardware address which we need to close up. */ bcopy(ar_tpa(ah), ar_tha(ah), ah->ar_pln); m_adj(m, -ah->ar_hln); break; } #endif #ifdef INET6 case AF_INET6: if (unicast) { error = nd6_storelladdr(&fc->fc_if, rt, m, dst, (u_char *) destfw); if (error) return (error); } type = ETHERTYPE_IPV6; break; #endif default: if_printf(ifp, "can't handle af%d\n", dst->sa_family); error = EAFNOSUPPORT; goto bad; } /* * Let BPF tap off a copy before we encapsulate. */ if (ifp->if_bpf) { struct fw_bpfhdr h; if (unicast) bcopy(destfw, h.firewire_dhost, 8); else bcopy(&firewire_broadcastaddr, h.firewire_dhost, 8); bcopy(&fc->fc_hwaddr, h.firewire_shost, 8); h.firewire_type = htons(type); bpf_mtap2(ifp->if_bpf, &h, sizeof(h), m); } /* * Punt on MCAP for now and send all multicast packets on the * broadcast channel. */ if (m->m_flags & M_MCAST) m->m_flags |= M_BCAST; /* * Figure out what speed to use and what the largest supported * packet size is. For unicast, this is the minimum of what we * can speak and what they can hear. For broadcast, lets be * conservative and use S100. We could possibly improve that * by examining the bus manager's speed map or similar. We * also reduce the packet size for broadcast to account for * the GASP header. */ if (unicast) { speed = min(fc->fc_speed, destfw->sspd); psize = min(512 << speed, 2 << destfw->sender_max_rec); } else { speed = 0; psize = 512 - 2*sizeof(uint32_t); } /* * Next, we encapsulate, possibly fragmenting the original * datagram if it won't fit into a single packet. */ if (m->m_pkthdr.len <= psize - sizeof(uint32_t)) { /* * No fragmentation is necessary. */ M_PREPEND(m, sizeof(uint32_t), M_DONTWAIT); if (!m) { error = ENOBUFS; goto bad; } enc = mtod(m, union fw_encap *); enc->unfrag.ether_type = type; enc->unfrag.lf = FW_ENCAP_UNFRAG; /* * Byte swap the encapsulation header manually. */ enc->ul[0] = htonl(enc->ul[0]); - return (IF_HANDOFF(&ifp->if_snd, m, ifp) ? 0 : ENOBUFS); + IFQ_HANDOFF(ifp, m, error); + return (error); } else { /* * Fragment the datagram, making sure to leave enough * space for the encapsulation header in each packet. */ fsize = psize - 2*sizeof(uint32_t); dgl = next_dgl++; dsize = m->m_pkthdr.len; foff = 0; while (m) { if (m->m_pkthdr.len > fsize) { /* * Split off the tail segment from the * datagram, copying our tags over. */ mtail = m_split(m, fsize, M_DONTWAIT); m_tag_copy_chain(mtail, m, M_NOWAIT); } else { mtail = 0; } /* * Add our encapsulation header to this * fragment and hand it off to the link. */ M_PREPEND(m, 2*sizeof(uint32_t), M_DONTWAIT); if (!m) { error = ENOBUFS; goto bad; } enc = mtod(m, union fw_encap *); if (foff == 0) { enc->firstfrag.lf = FW_ENCAP_FIRST; enc->firstfrag.datagram_size = dsize - 1; enc->firstfrag.ether_type = type; enc->firstfrag.dgl = dgl; } else { if (mtail) enc->nextfrag.lf = FW_ENCAP_NEXT; else enc->nextfrag.lf = FW_ENCAP_LAST; enc->nextfrag.datagram_size = dsize - 1; enc->nextfrag.fragment_offset = foff; enc->nextfrag.dgl = dgl; } foff += m->m_pkthdr.len - 2*sizeof(uint32_t); /* * Byte swap the encapsulation header manually. */ enc->ul[0] = htonl(enc->ul[0]); enc->ul[1] = htonl(enc->ul[1]); - if (!IF_HANDOFF(&ifp->if_snd, m, ifp)) { + IFQ_HANDOFF(ifp, m, error); + if (error) { if (mtail) m_freem(mtail); return (ENOBUFS); } m = mtail; } return (0); } bad: if (m) m_freem(m); return (error); } static struct mbuf * firewire_input_fragment(struct fw_com *fc, struct mbuf *m, int src) { union fw_encap *enc; struct fw_reass *r; struct mbuf *mf, *mprev; int dsize; int fstart, fend, start, end, islast; uint32_t id; GIANT_REQUIRED; /* * Find an existing reassembly buffer or create a new one. */ enc = mtod(m, union fw_encap *); id = enc->firstfrag.dgl | (src << 16); STAILQ_FOREACH(r, &fc->fc_frags, fr_link) if (r->fr_id == id) break; if (!r) { r = malloc(sizeof(struct fw_reass), M_TEMP, M_NOWAIT); if (!r) { m_freem(m); return 0; } r->fr_id = id; r->fr_frags = 0; STAILQ_INSERT_HEAD(&fc->fc_frags, r, fr_link); } /* * If this fragment overlaps any other fragment, we must discard * the partial reassembly and start again. */ if (enc->firstfrag.lf == FW_ENCAP_FIRST) fstart = 0; else fstart = enc->nextfrag.fragment_offset; fend = fstart + m->m_pkthdr.len - 2*sizeof(uint32_t); dsize = enc->nextfrag.datagram_size; islast = (enc->nextfrag.lf == FW_ENCAP_LAST); for (mf = r->fr_frags; mf; mf = mf->m_nextpkt) { enc = mtod(mf, union fw_encap *); if (enc->nextfrag.datagram_size != dsize) { /* * This fragment must be from a different * packet. */ goto bad; } if (enc->firstfrag.lf == FW_ENCAP_FIRST) start = 0; else start = enc->nextfrag.fragment_offset; end = start + mf->m_pkthdr.len - 2*sizeof(uint32_t); if ((fstart < end && fend > start) || (islast && enc->nextfrag.lf == FW_ENCAP_LAST)) { /* * Overlap - discard reassembly buffer and start * again with this fragment. */ goto bad; } } /* * Find where to put this fragment in the list. */ for (mf = r->fr_frags, mprev = NULL; mf; mprev = mf, mf = mf->m_nextpkt) { enc = mtod(mf, union fw_encap *); if (enc->firstfrag.lf == FW_ENCAP_FIRST) start = 0; else start = enc->nextfrag.fragment_offset; if (start >= fend) break; } /* * If this is a last fragment and we are not adding at the end * of the list, discard the buffer. */ if (islast && mprev && mprev->m_nextpkt) goto bad; if (mprev) { m->m_nextpkt = mprev->m_nextpkt; mprev->m_nextpkt = m; /* * Coalesce forwards and see if we can make a whole * datagram. */ enc = mtod(mprev, union fw_encap *); if (enc->firstfrag.lf == FW_ENCAP_FIRST) start = 0; else start = enc->nextfrag.fragment_offset; end = start + mprev->m_pkthdr.len - 2*sizeof(uint32_t); while (end == fstart) { /* * Strip off the encap header from m and * append it to mprev, freeing m. */ m_adj(m, 2*sizeof(uint32_t)); mprev->m_nextpkt = m->m_nextpkt; mprev->m_pkthdr.len += m->m_pkthdr.len; m_cat(mprev, m); if (mprev->m_pkthdr.len == dsize + 1 + 2*sizeof(uint32_t)) { /* * We have assembled a complete packet * we must be finished. Make sure we have * merged the whole chain. */ STAILQ_REMOVE(&fc->fc_frags, r, fw_reass, fr_link); free(r, M_TEMP); m = mprev->m_nextpkt; while (m) { mf = m->m_nextpkt; m_freem(m); m = mf; } mprev->m_nextpkt = NULL; return (mprev); } /* * See if we can continue merging forwards. */ end = fend; m = mprev->m_nextpkt; if (m) { enc = mtod(m, union fw_encap *); if (enc->firstfrag.lf == FW_ENCAP_FIRST) fstart = 0; else fstart = enc->nextfrag.fragment_offset; fend = fstart + m->m_pkthdr.len - 2*sizeof(uint32_t); } else { break; } } } else { m->m_nextpkt = 0; r->fr_frags = m; } return (0); bad: while (r->fr_frags) { mf = r->fr_frags; r->fr_frags = mf->m_nextpkt; m_freem(mf); } m->m_nextpkt = 0; r->fr_frags = m; return (0); } void firewire_input(struct ifnet *ifp, struct mbuf *m, uint16_t src) { struct fw_com *fc = (struct fw_com *) ifp; union fw_encap *enc; int type, isr; GIANT_REQUIRED; /* * The caller has already stripped off the packet header * (stream or wreqb) and marked the mbuf's M_BCAST flag * appropriately. We de-encapsulate the IP packet and pass it * up the line after handling link-level fragmentation. */ if (m->m_pkthdr.len < sizeof(uint32_t)) { if_printf(ifp, "discarding frame without " "encapsulation header (len %u pkt len %u)\n", m->m_len, m->m_pkthdr.len); } m = m_pullup(m, sizeof(uint32_t)); enc = mtod(m, union fw_encap *); /* * Byte swap the encapsulation header manually. */ enc->ul[0] = htonl(enc->ul[0]); if (enc->unfrag.lf != 0) { m = m_pullup(m, 2*sizeof(uint32_t)); if (!m) return; enc = mtod(m, union fw_encap *); enc->ul[1] = htonl(enc->ul[1]); m = firewire_input_fragment(fc, m, src); if (!m) return; enc = mtod(m, union fw_encap *); type = enc->firstfrag.ether_type; m_adj(m, 2*sizeof(uint32_t)); } else { type = enc->unfrag.ether_type; m_adj(m, sizeof(uint32_t)); } if (m->m_pkthdr.rcvif == NULL) { if_printf(ifp, "discard frame w/o interface pointer\n"); ifp->if_ierrors++; m_freem(m); return; } #ifdef DIAGNOSTIC if (m->m_pkthdr.rcvif != ifp) { if_printf(ifp, "Warning, frame marked as received on %s\n", m->m_pkthdr.rcvif->if_xname); } #endif #ifdef MAC /* * Tag the mbuf with an appropriate MAC label before any other * consumers can get to it. */ mac_create_mbuf_from_ifnet(ifp, m); #endif /* * Give bpf a chance at the packet. The link-level driver * should have left us a tag with the EUID of the sender. */ if (ifp->if_bpf) { struct fw_bpfhdr h; struct m_tag *mtag; mtag = m_tag_locate(m, MTAG_FIREWIRE, MTAG_FIREWIRE_SENDER_EUID, 0); if (mtag) bcopy(mtag + 1, h.firewire_shost, 8); else bcopy(&firewire_broadcastaddr, h.firewire_dhost, 8); bcopy(&fc->fc_hwaddr, h.firewire_dhost, 8); h.firewire_type = htons(type); bpf_mtap2(ifp->if_bpf, &h, sizeof(h), m); } if (ifp->if_flags & IFF_MONITOR) { /* * Interface marked for monitoring; discard packet. */ m_freem(m); return; } ifp->if_ibytes += m->m_pkthdr.len; /* Discard packet if interface is not up */ if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } if (m->m_flags & (M_BCAST|M_MCAST)) ifp->if_imcasts++; switch (type) { #ifdef INET case ETHERTYPE_IP: if (ip_fastforward(m)) return; isr = NETISR_IP; break; case ETHERTYPE_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); /* * Adjust the arp packet to insert an empty tha slot. */ m->m_len += ah->ar_hln; m->m_pkthdr.len += ah->ar_hln; bcopy(ar_tha(ah), ar_tpa(ah), ah->ar_pln); isr = NETISR_ARP; break; } #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; break; #endif default: m_freem(m); return; } netisr_dispatch(isr, m); } int firewire_ioctl(struct ifnet *ifp, int command, caddr_t data) { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(&IFP2FC(ifp)->fc_hwaddr, (caddr_t) sa->sa_data, sizeof(struct fw_hwaddr)); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > 1500) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; default: error = EINVAL; /* XXX netbsd has ENOTTY??? */ break; } return (error); } static int firewire_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa, struct sockaddr *sa) { #ifdef INET struct sockaddr_in *sin; #endif #ifdef INET6 struct sockaddr_in6 *sin6; #endif switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. */ *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; *llsa = 0; 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; *llsa = 0; return 0; #endif default: /* * Well, the text isn't quite right, but it's the name * that counts... */ return EAFNOSUPPORT; } } void firewire_ifattach(struct ifnet *ifp, struct fw_hwaddr *llc) { struct fw_com *fc = (struct fw_com *) ifp; struct ifaddr *ifa; struct sockaddr_dl *sdl; static const char* speeds[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" }; fc->fc_speed = llc->sspd; STAILQ_INIT(&fc->fc_frags); ifp->if_type = IFT_IEEE1394; ifp->if_addrlen = sizeof(struct fw_hwaddr); ifp->if_hdrlen = 0; if_attach(ifp); ifp->if_mtu = 1500; /* XXX */ ifp->if_output = firewire_output; ifp->if_resolvemulti = firewire_resolvemulti; ifp->if_broadcastaddr = (u_char *) &firewire_broadcastaddr; ifa = ifaddr_byindex(ifp->if_index); KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_IEEE1394; sdl->sdl_alen = ifp->if_addrlen; bcopy(llc, LLADDR(sdl), ifp->if_addrlen); bpfattach(ifp, DLT_APPLE_IP_OVER_IEEE1394, sizeof(struct fw_hwaddr)); if_printf(ifp, "Firewire address: %8D @ 0x%04x%08x, %s, maxrec %d\n", (uint8_t *) &llc->sender_unique_ID_hi, ":", ntohs(llc->sender_unicast_FIFO_hi), ntohl(llc->sender_unicast_FIFO_lo), speeds[llc->sspd], (2 << llc->sender_max_rec)); } void firewire_ifdetach(struct ifnet *ifp) { bpfdetach(ifp); if_detach(ifp); } void firewire_busreset(struct ifnet *ifp) { struct fw_com *fc = (struct fw_com *) ifp; struct fw_reass *r; struct mbuf *m; /* * Discard any partial datagrams since the host ids may have changed. */ while ((r = STAILQ_FIRST(&fc->fc_frags))) { STAILQ_REMOVE_HEAD(&fc->fc_frags, fr_link); while (r->fr_frags) { m = r->fr_frags; r->fr_frags = m->m_nextpkt; m_freem(m); } free(r, M_TEMP); } } Index: head/sys/net/if_iso88025subr.c =================================================================== --- head/sys/net/if_iso88025subr.c (revision 130548) +++ head/sys/net/if_iso88025subr.c (revision 130549) @@ -1,783 +1,784 @@ /* * Copyright (c) 1998, Larry Lile * All rights reserved. * * For latest sources and information on this driver, please * go to http://anarchy.stdio.com. * * Questions, comments or suggestions should be directed to * Larry Lile . * * 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 unmodified, 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$ * */ /* * * General ISO 802.5 (Token Ring) support routines * */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #endif #ifdef INET6 #include #endif #ifdef IPX #include #include #endif static const u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int iso88025_resolvemulti (struct ifnet *, struct sockaddr **, struct sockaddr *); #define senderr(e) do { error = (e); goto bad; } while (0) /* * Perform common duties while attaching to interface list */ void iso88025_ifattach(struct ifnet *ifp, int bpf) { struct ifaddr *ifa; struct sockaddr_dl *sdl; ifa = NULL; ifp->if_type = IFT_ISO88025; ifp->if_addrlen = ISO88025_ADDR_LEN; ifp->if_hdrlen = ISO88025_HDR_LEN; if_attach(ifp); /* Must be called before additional assignments */ ifp->if_output = iso88025_output; ifp->if_input = iso88025_input; ifp->if_resolvemulti = iso88025_resolvemulti; ifp->if_broadcastaddr = iso88025_broadcastaddr; if (ifp->if_baudrate == 0) ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */ if (ifp->if_mtu == 0) ifp->if_mtu = ISO88025_DEFAULT_MTU; ifa = ifaddr_byindex(ifp->if_index); if (ifa == 0) { if_printf(ifp, "%s() no lladdr!\n", __func__); return; } sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ISO88025; sdl->sdl_alen = ifp->if_addrlen; bcopy(IFP2AC(ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); if (bpf) bpfattach(ifp, DLT_IEEE802, ISO88025_HDR_LEN); return; } /* * Perform common duties while detaching a Token Ring interface */ void iso88025_ifdetach(ifp, bpf) struct ifnet *ifp; int bpf; { if (bpf) bpfdetach(ifp); if_detach(ifp); return; } int iso88025_ioctl(struct ifnet *ifp, int command, caddr_t data) { struct ifaddr *ifa; struct ifreq *ifr; int error; ifa = (struct ifaddr *) data; ifr = (struct ifreq *) data; error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif /* INET */ #ifdef IPX /* * XXX - This code is probably wrong */ case AF_IPX: { struct ipx_addr *ina; struct arpcom *ac; ina = &(IA_SIPX(ifa)->sipx_addr); ac = IFP2AC(ifp); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) ac->ac_enaddr; else bcopy((caddr_t) ina->x_host.c_host, (caddr_t) ac->ac_enaddr, ISO88025_ADDR_LEN); /* * Set new address */ ifp->if_init(ifp->if_softc); } break; #endif /* IPX */ default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t) sa->sa_data, ISO88025_ADDR_LEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ISO88025_MAX_MTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; default: error = EINVAL; /* XXX netbsd has ENOTTY??? */ break; } return (error); } /* * ISO88025 encapsulation */ int iso88025_output(ifp, m, dst, rt0) struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt0; { u_int16_t snap_type = 0; int loop_copy = 0, error = 0, rif_len = 0; u_char edst[ISO88025_ADDR_LEN]; struct iso88025_header *th; struct iso88025_header gen_th; struct sockaddr_dl *sdl = NULL; struct rtentry *rt; #ifdef MAC error = mac_check_ifnet_transmit(ifp, m); if (error) senderr(error); #endif if (ifp->if_flags & IFF_MONITOR) senderr(ENETDOWN); if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); getmicrotime(&ifp->if_lastchange); /* Calculate routing info length based on arp table entry */ /* XXX any better way to do this ? */ error = rt_check(&rt, &rt0, dst); if (error) goto bad; if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway)) if (SDL_ISO88025(sdl)->trld_rcf != 0) rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf); /* Generate a generic 802.5 header for the packet */ gen_th.ac = TR_AC; gen_th.fc = TR_LLC_FRAME; (void)memcpy((caddr_t)gen_th.iso88025_shost, IFP2AC(ifp)->ac_enaddr, ISO88025_ADDR_LEN); if (rif_len) { gen_th.iso88025_shost[0] |= TR_RII; if (rif_len > 2) { gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf; (void)memcpy((caddr_t)gen_th.rd, (caddr_t)SDL_ISO88025(sdl)->trld_route, rif_len - 2); } } switch (dst->sa_family) { #ifdef INET case AF_INET: error = arpresolve(ifp, rt0, m, dst, edst); if (error) return (error == EWOULDBLOCK ? 0 : error); snap_type = ETHERTYPE_IP; break; case AF_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_IEEE802); loop_copy = -1; /* if this is for us, don't do it */ switch(ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: snap_type = ETHERTYPE_REVARP; break; case ARPOP_REQUEST: case ARPOP_REPLY: default: snap_type = ETHERTYPE_ARP; break; } if (m->m_flags & M_BCAST) bcopy(ifp->if_broadcastaddr, edst, ISO88025_ADDR_LEN); else bcopy(ar_tha(ah), edst, ISO88025_ADDR_LEN); } break; #endif /* INET */ #ifdef INET6 case AF_INET6: error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst); if (error) return (error); snap_type = ETHERTYPE_IPV6; break; #endif /* INET6 */ #ifdef IPX case AF_IPX: { u_int8_t *cp; bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst, ISO88025_ADDR_LEN); M_PREPEND(m, 3, M_TRYWAIT); if (m == 0) senderr(ENOBUFS); m = m_pullup(m, 3); if (m == 0) senderr(ENOBUFS); cp = mtod(m, u_int8_t *); *cp++ = ETHERTYPE_IPX_8022; *cp++ = ETHERTYPE_IPX_8022; *cp++ = LLC_UI; } break; #endif /* IPX */ case AF_UNSPEC: { struct iso88025_sockaddr_data *sd; /* * For AF_UNSPEC sockaddr.sa_data must contain all of the * mac information needed to send the packet. This allows * full mac, llc, and source routing function to be controlled. * llc and source routing information must already be in the * mbuf provided, ac/fc are set in sa_data. sockaddr.sa_data * should be an iso88025_sockaddr_data structure see iso88025.h */ loop_copy = -1; sd = (struct iso88025_sockaddr_data *)dst->sa_data; gen_th.ac = sd->ac; gen_th.fc = sd->fc; (void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost, ISO88025_ADDR_LEN); (void)memcpy((caddr_t)gen_th.iso88025_shost, (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN); rif_len = 0; break; } default: if_printf(ifp, "can't handle af%d\n", dst->sa_family); senderr(EAFNOSUPPORT); break; } /* * Add LLC header. */ if (snap_type != 0) { struct llc *l; M_PREPEND(m, LLC_SNAPFRAMELEN, 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; l->llc_snap.ether_type = htons(snap_type); } /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT); if (m == 0) senderr(ENOBUFS); th = mtod(m, struct iso88025_header *); bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN); /* Copy as much of the generic header as is needed into the mbuf */ memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len); /* * 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 > 0)) { struct mbuf *n; n = m_copy(m, 0, (int)M_COPYALL); (void) if_simloop(ifp, n, dst->sa_family, ISO88025_HDR_LEN); } else if (bcmp(th->iso88025_dhost, th->iso88025_shost, ETHER_ADDR_LEN) == 0) { (void) if_simloop(ifp, m, dst->sa_family, ISO88025_HDR_LEN); return(0); /* XXX */ } } - if (! IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN) ) { + IFQ_HANDOFF_ADJ(ifp, m, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN, error); + if (error) { printf("iso88025_output: packet dropped QFULL.\n"); - senderr(ENOBUFS); + ifp->if_oerrors++; } return (error); bad: ifp->if_oerrors++; if (m) m_freem(m); return (error); } /* * ISO 88025 de-encapsulation */ void iso88025_input(ifp, m) struct ifnet *ifp; struct mbuf *m; { struct iso88025_header *th; struct llc *l; int isr; int mac_hdr_len; /* * Do consistency checks to verify assumptions * made by code past this point. */ if ((m->m_flags & M_PKTHDR) == 0) { if_printf(ifp, "discard frame w/o packet header\n"); ifp->if_ierrors++; m_freem(m); return; } if (m->m_pkthdr.rcvif == NULL) { if_printf(ifp, "discard frame w/o interface pointer\n"); ifp->if_ierrors++; m_freem(m); return; } m = m_pullup(m, ISO88025_HDR_LEN); if (m == NULL) { ifp->if_ierrors++; goto dropanyway; } th = mtod(m, struct iso88025_header *); m->m_pkthdr.header = (void *)th; /* * Discard packet if interface is not up. */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) goto dropanyway; /* * Give bpf a chance at the packet. */ BPF_MTAP(ifp, m); /* * Interface marked for monitoring; discard packet. */ if (ifp->if_flags & IFF_MONITOR) { m_freem(m); return; } #ifdef MAC mac_create_mbuf_from_ifnet(ifp, m); #endif /* * Update interface statistics. */ ifp->if_ibytes += m->m_pkthdr.len; getmicrotime(&ifp->if_lastchange); /* * Discard non local unicast packets when interface * is in promiscuous mode. */ if ((ifp->if_flags & IFF_PROMISC) && ((th->iso88025_dhost[0] & 1) == 0) && (bcmp(IFP2AC(ifp)->ac_enaddr, (caddr_t) th->iso88025_dhost, ISO88025_ADDR_LEN) != 0)) goto dropanyway; /* * Set mbuf flags for bcast/mcast. */ if (th->iso88025_dhost[0] & 1) { if (bcmp(iso88025_broadcastaddr, th->iso88025_dhost, ISO88025_ADDR_LEN) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; ifp->if_imcasts++; } mac_hdr_len = ISO88025_HDR_LEN; /* Check for source routing info */ if (th->iso88025_shost[0] & TR_RII) mac_hdr_len += TR_RCF_RIFLEN(th->rcf); /* Strip off ISO88025 header. */ m_adj(m, mac_hdr_len); m = m_pullup(m, LLC_SNAPFRAMELEN); if (m == 0) { ifp->if_ierrors++; goto dropanyway; } l = mtod(m, struct llc *); switch (l->llc_dsap) { #ifdef IPX case ETHERTYPE_IPX_8022: /* Thanks a bunch Novell */ if ((l->llc_control != LLC_UI) || (l->llc_ssap != ETHERTYPE_IPX_8022)) { ifp->if_noproto++; goto dropanyway; } th->iso88025_shost[0] &= ~(TR_RII); m_adj(m, 3); isr = NETISR_IPX; break; #endif /* IPX */ case LLC_SNAP_LSAP: { u_int16_t type; if ((l->llc_control != LLC_UI) || (l->llc_ssap != LLC_SNAP_LSAP)) { ifp->if_noproto++; goto dropanyway; } if (l->llc_snap.org_code[0] != 0 || l->llc_snap.org_code[1] != 0 || l->llc_snap.org_code[2] != 0) { ifp->if_noproto++; goto dropanyway; } type = ntohs(l->llc_snap.ether_type); m_adj(m, LLC_SNAPFRAMELEN); switch (type) { #ifdef INET case ETHERTYPE_IP: th->iso88025_shost[0] &= ~(TR_RII); if (ip_fastforward(m)) return; isr = NETISR_IP; break; case ETHERTYPE_ARP: if (ifp->if_flags & IFF_NOARP) goto dropanyway; isr = NETISR_ARP; break; #endif /* INET */ #ifdef IPX_SNAP /* XXX: Not supported! */ case ETHERTYPE_IPX: th->iso88025_shost[0] &= ~(TR_RII); isr = NETISR_IPX; break; #endif /* IPX_SNAP */ #ifdef INET6 case ETHERTYPE_IPV6: th->iso88025_shost[0] &= ~(TR_RII); isr = NETISR_IPV6; break; #endif /* INET6 */ default: printf("iso88025_input: unexpected llc_snap ether_type 0x%02x\n", type); ifp->if_noproto++; goto dropanyway; } break; } #ifdef ISO case LLC_ISO_LSAP: switch (l->llc_control) { case LLC_UI: ifp->if_noproto++; goto dropanyway; break; case LLC_XID: case LLC_XID_P: if(m->m_len < ISO88025_ADDR_LEN) goto dropanyway; l->llc_window = 0; l->llc_fid = 9; l->llc_class = 1; l->llc_dsap = l->llc_ssap = 0; /* Fall through to */ case LLC_TEST: case LLC_TEST_P: { struct sockaddr sa; struct arpcom *ac; struct iso88025_sockaddr_data *th2; int i; u_char c; ac = IFP2AC(ifp); c = l->llc_dsap; if (th->iso88025_shost[0] & TR_RII) { /* XXX */ printf("iso88025_input: dropping source routed LLC_TEST\n"); goto dropanyway; } l->llc_dsap = l->llc_ssap; l->llc_ssap = c; if (m->m_flags & (M_BCAST | M_MCAST)) bcopy((caddr_t)ac->ac_enaddr, (caddr_t)th->iso88025_dhost, ISO88025_ADDR_LEN); sa.sa_family = AF_UNSPEC; sa.sa_len = sizeof(sa); th2 = (struct iso88025_sockaddr_data *)sa.sa_data; for (i = 0; i < ISO88025_ADDR_LEN; i++) { th2->ether_shost[i] = c = th->iso88025_dhost[i]; th2->ether_dhost[i] = th->iso88025_dhost[i] = th->iso88025_shost[i]; th->iso88025_shost[i] = c; } th2->ac = TR_AC; th2->fc = TR_LLC_FRAME; ifp->if_output(ifp, m, &sa, NULL); return; } default: printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control); ifp->if_noproto++; goto dropanyway; break; } break; #endif /* ISO */ default: printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap); ifp->if_noproto++; goto dropanyway; break; } netisr_dispatch(isr, m); return; dropanyway: ifp->if_iqdrops++; if (m) m_freem(m); return; } static int iso88025_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|M_ZERO); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ISO88025; sdl->sdl_alen = ISO88025_ADDR_LEN; 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|M_ZERO); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ISO88025; sdl->sdl_alen = ISO88025_ADDR_LEN; 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); } return (0); } static moduledata_t iso88025_mod = { "iso88025", NULL, 0 }; DECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(iso88025, 1); Index: head/sys/net/if_sl.c =================================================================== --- head/sys/net/if_sl.c (revision 130548) +++ head/sys/net/if_sl.c (revision 130549) @@ -1,1114 +1,1115 @@ /* * Copyright (c) 1987, 1989, 1992, 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. * 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_sl.c 8.6 (Berkeley) 2/1/94 * $FreeBSD$ */ /* * Serial Line interface * * Rick Adams * Center for Seismic Studies * 1300 N 17th Street, Suite 1450 * Arlington, Virginia 22209 * (703)276-7900 * rick@seismo.ARPA * seismo!rick * * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). * N.B.: this belongs in netinet, not net, the way it stands now. * Should have a link-layer type designation, but wouldn't be * backwards-compatible. * * Converted to 4.3BSD Beta by Chris Torek. * Other changes made at Berkeley, based in part on code by Kirk Smith. * W. Jolitz added slip abort. * * Hacked almost beyond recognition by Van Jacobson (van@helios.ee.lbl.gov). * Added priority queuing for "interactive" traffic; hooks for TCP * header compression; ICMP filtering (at 2400 baud, some cretin * pinging you can use up all your bandwidth). Made low clist behavior * more robust and slightly less likely to hang serial line. * Sped up a bunch of things. * * Note that splimp() is used throughout to block both (tty) input * interrupts and network activity; thus, splimp must be >= spltty. */ #include "opt_inet.h" #include "opt_slip.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if INET #include #include #include #include #else #error "Huh? Slip without inet?" #endif #include #include #include #include static MALLOC_DEFINE(M_SL, "sl", "SLIP Interface"); /* * SLRMAX is a hard limit on input packet size. To simplify the code * and improve performance, we require that packets fit in an mbuf * cluster, and if we get a compressed packet, there's enough extra * room to expand the header into a max length tcp/ip header (128 * bytes). So, SLRMAX can be at most * MCLBYTES - 128 * * SLMTU is the default transmit MTU. The transmit MTU should be kept * small enough so that interactive use doesn't suffer, but large * enough to provide good performance. 552 is a good choice for SLMTU * because it is high enough to not fragment TCP packets being routed * through this host. Packet fragmentation is bad with SLIP because * fragment headers aren't compressed. The previous assumptions about * the best MTU value don't really hold when using modern modems with * BTLZ data compression because the modem buffers play a much larger * role in interactive performance than the MTU. The MTU can be changed * at any time to suit the specific environment with ifconfig(8), and * its maximum value is defined as SLTMAX. SLTMAX must not be so large * that it would overflow the stack if BPF is configured (XXX; if_ppp.c * handles this better). * * SLIP_HIWAT is the amount of data that will be queued 'downstream' * of us (i.e., in clists waiting to be picked up by the tty output * interrupt). If we queue a lot of data downstream, it's immune to * our t.o.s. queuing. * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed * telnet/ftp will see a 1 sec wait, independent of the mtu (the * wait is dependent on the ftp window size but that's typically * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize * the cost (in idle time on the wire) of the tty driver running * off the end of its clists & having to call back slstart for a * new packet. For a tty interface with any buffering at all, this * cost will be zero. Even with a totally brain dead interface (like * the one on a typical workstation), the cost will be <= 1 character * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose * at most 1% while maintaining good interactive response. */ #define BUFOFFSET (128+sizeof(struct ifnet **)+SLIP_HDRLEN) #define SLRMAX (MCLBYTES - BUFOFFSET) #define SLBUFSIZE (SLRMAX + BUFOFFSET) #ifndef SLMTU #define SLMTU 552 /* default MTU */ #endif #define SLTMAX 1500 /* maximum MTU */ #define SLIP_HIWAT roundup(50,CBSIZE) #define CLISTRESERVE 1024 /* Can't let clists get too low */ /* * SLIP ABORT ESCAPE MECHANISM: * (inspired by HAYES modem escape arrangement) * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape } * within window time signals a "soft" exit from slip mode by remote end * if the IFF_DEBUG flag is on. */ #define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/ #define ABT_IDLE 1 /* in seconds - idle before an escape */ #define ABT_COUNT 3 /* count of escapes for abort */ #define ABT_WINDOW (ABT_COUNT*2+2) /* in seconds - time to count */ static LIST_HEAD(sl_list, sl_softc) sl_list; #define FRAME_END 0xc0 /* Frame End */ #define FRAME_ESCAPE 0xdb /* Frame Esc */ #define TRANS_FRAME_END 0xdc /* transposed frame end */ #define TRANS_FRAME_ESCAPE 0xdd /* transposed frame esc */ static int slisstatic(int); static void slmarkstatic(int); static struct sl_softc *slcreate(void); static void sldestroy(struct sl_softc *sc); static struct mbuf *sl_btom(struct sl_softc *, int); static timeout_t sl_keepalive; static timeout_t sl_outfill; static l_close_t slclose; static l_rint_t slinput; static l_ioctl_t sltioctl; static int slioctl(struct ifnet *, u_long, caddr_t); static int slopen(dev_t, struct tty *); static int sloutput(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); static int slstart(struct tty *); static struct linesw slipdisc = { slopen, slclose, l_noread, l_nowrite, sltioctl, slinput, slstart, ttymodem, FRAME_END }; /* * Called from boot code to establish sl interfaces. */ static int sl_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: ldisc_register(SLIPDISC, &slipdisc); LIST_INIT(&sl_list); break; case MOD_UNLOAD: ldisc_deregister(SLIPDISC); printf("if_sl module unload - not possible for this module type\n"); return EINVAL; } return 0; } static moduledata_t sl_mod = { "if_sl", sl_modevent, 0 }; DECLARE_MODULE(if_sl, sl_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); static int *st_unit_list; static size_t st_unit_max = 0; static int slisunitfree(int unit) { struct sl_softc *nc; LIST_FOREACH(nc, &sl_list, sl_next) { if (nc->sc_if.if_dunit == unit) return (0); } return (1); } static int slisstatic(unit) int unit; { size_t i; for (i = 0; i < st_unit_max; i++) if (st_unit_list[i] == unit) return 1; return 0; } static void slmarkstatic(unit) int unit; { int *t; if (slisstatic(unit)) return; MALLOC(t, int *, sizeof(int) * (st_unit_max+1), M_SL, M_NOWAIT); if (t == NULL) return; if (st_unit_list) { bcopy(st_unit_list, t, sizeof(int) * st_unit_max); free(st_unit_list, M_SL); } st_unit_list = t; st_unit_list[st_unit_max] = unit; st_unit_max++; } static struct sl_softc * slcreate() { struct sl_softc *sc; int unit; struct mbuf *m; MALLOC(sc, struct sl_softc *, sizeof(*sc), M_SL, M_WAITOK | M_ZERO); m = m_gethdr(M_TRYWAIT, MT_DATA); if (m != NULL) { MCLGET(m, M_TRYWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); m = NULL; } } if (m == NULL) { printf("sl: can't allocate buffer\n"); free(sc, M_SL); return (NULL); } sc->sc_ep = mtod(m, u_char *) + SLBUFSIZE; sc->sc_mbuf = m; sc->sc_buf = sc->sc_ep - SLRMAX; sc->sc_mp = sc->sc_buf; sl_compress_init(&sc->sc_comp, -1); sc->sc_if.if_softc = sc; sc->sc_if.if_mtu = SLMTU; sc->sc_if.if_flags = #ifdef SLIP_IFF_OPTS SLIP_IFF_OPTS; #else IFF_POINTOPOINT | SC_AUTOCOMP | IFF_MULTICAST; #endif sc->sc_if.if_type = IFT_SLIP; sc->sc_if.if_ioctl = slioctl; sc->sc_if.if_output = sloutput; sc->sc_if.if_snd.ifq_maxlen = 50; sc->sc_fastq.ifq_maxlen = 32; sc->sc_if.if_linkmib = sc; sc->sc_if.if_linkmiblen = sizeof *sc; mtx_init(&sc->sc_fastq.ifq_mtx, "sl_fastq", NULL, MTX_DEF); /* * Find a suitable unit number. */ for (unit=0; ; unit++) { if (slisstatic(unit)) continue; if (!slisunitfree(unit)) continue; break; } if_initname(&sc->sc_if, "sl", unit); LIST_INSERT_HEAD(&sl_list, sc, sl_next); if_attach(&sc->sc_if); bpfattach(&sc->sc_if, DLT_SLIP, SLIP_HDRLEN); return sc; } /* * Line specific open routine. * Attach the given tty to the first available sl unit. */ /* ARGSUSED */ static int slopen(dev, tp) dev_t dev; register struct tty *tp; { register struct sl_softc *sc; int s, error; error = suser(curthread); if (error) return (error); if (tp->t_line == SLIPDISC) return (0); if ((sc = slcreate()) == NULL) return (ENOBUFS); tp->t_sc = (caddr_t)sc; sc->sc_ttyp = tp; sc->sc_if.if_baudrate = tp->t_ospeed; ttyflush(tp, FREAD | FWRITE); tp->t_line = SLIPDISC; /* * We don't use t_canq or t_rawq, so reduce their * cblock resources to 0. Reserve enough cblocks * for t_outq to guarantee that we can fit a full * packet if the SLIP_HIWAT check allows slstart() * to loop. Use the same value for the cblock * limit since the reserved blocks should always * be enough. Reserving cblocks probably makes * the CLISTRESERVE check unnecessary and wasteful. */ clist_alloc_cblocks(&tp->t_canq, 0, 0); clist_alloc_cblocks(&tp->t_outq, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1); clist_alloc_cblocks(&tp->t_rawq, 0, 0); s = splnet(); if_up(&sc->sc_if); splx(s); return (0); } static void sldestroy(struct sl_softc *sc) { bpfdetach(&sc->sc_if); if_detach(&sc->sc_if); LIST_REMOVE(sc, sl_next); m_free(sc->sc_mbuf); mtx_destroy(&sc->sc_fastq.ifq_mtx); if (sc->bpfbuf) free(sc->bpfbuf, M_SL); free(sc, M_SL); } /* * Line specific close routine. * Detach the tty from the sl unit. */ static int slclose(tp,flag) struct tty *tp; int flag; { register struct sl_softc *sc; int s; ttyflush(tp, FREAD | FWRITE); /* * XXX the placement of the following spl is misleading. tty * interrupts must be blocked across line discipline switches * and throughout closes to avoid races. */ s = splimp(); /* actually, max(spltty, splnet) */ clist_free_cblocks(&tp->t_outq); tp->t_line = 0; sc = (struct sl_softc *)tp->t_sc; if (sc != NULL) { if (sc->sc_outfill) { sc->sc_outfill = 0; untimeout(sl_outfill, sc, sc->sc_ofhandle); } if (sc->sc_keepalive) { sc->sc_keepalive = 0; untimeout(sl_keepalive, sc, sc->sc_kahandle); } if_down(&sc->sc_if); sc->sc_ttyp = NULL; tp->t_sc = NULL; sldestroy(sc); } splx(s); return 0; } /* * Line specific (tty) ioctl routine. * Provide a way to get the sl unit number. */ /* ARGSUSED */ static int sltioctl(tp, cmd, data, flag, td) struct tty *tp; u_long cmd; caddr_t data; int flag; struct thread *td; { struct sl_softc *sc = (struct sl_softc *)tp->t_sc; int s, unit, wasup; s = splimp(); switch (cmd) { case SLIOCGUNIT: *(int *)data = sc->sc_if.if_dunit; break; case SLIOCSUNIT: unit = *(u_int *)data; if (unit < 0) { splx(s); return (ENXIO); } if (sc->sc_if.if_dunit != unit) { if (!slisunitfree(unit)) { splx(s); return (ENXIO); } wasup = sc->sc_if.if_flags & IFF_UP; bpfdetach(&sc->sc_if); if_detach(&sc->sc_if); LIST_REMOVE(sc, sl_next); if_initname(&sc->sc_if, "sl", unit); LIST_INSERT_HEAD(&sl_list, sc, sl_next); if_attach(&sc->sc_if); bpfattach(&sc->sc_if, DLT_SLIP, SLIP_HDRLEN); if (wasup) if_up(&sc->sc_if); else if_down(&sc->sc_if); clist_alloc_cblocks(&tp->t_outq, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1, SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1); } slmarkstatic(unit); break; case SLIOCSKEEPAL: sc->sc_keepalive = *(u_int *)data * hz; if (sc->sc_keepalive) { sc->sc_flags |= SC_KEEPALIVE; sc->sc_kahandle = timeout(sl_keepalive, sc, sc->sc_keepalive); } else { if ((sc->sc_flags & SC_KEEPALIVE) != 0) { untimeout(sl_keepalive, sc, sc->sc_kahandle); sc->sc_flags &= ~SC_KEEPALIVE; } } break; case SLIOCGKEEPAL: *(int *)data = sc->sc_keepalive / hz; break; case SLIOCSOUTFILL: sc->sc_outfill = *(u_int *)data * hz; if (sc->sc_outfill) { sc->sc_flags |= SC_OUTWAIT; sc->sc_ofhandle = timeout(sl_outfill, sc, sc->sc_outfill); } else { if ((sc->sc_flags & SC_OUTWAIT) != 0) { untimeout(sl_outfill, sc, sc->sc_ofhandle); sc->sc_flags &= ~SC_OUTWAIT; } } break; case SLIOCGOUTFILL: *(int *)data = sc->sc_outfill / hz; break; default: splx(s); return (ENOIOCTL); } splx(s); return (0); } /* * Queue a packet. Start transmission if not active. * Compression happens in slstart; if we do it here, IP TOS * will cause us to not compress "background" packets, because * ordering gets trashed. It can be done for all packets in slstart. */ static int sloutput(ifp, m, dst, rtp) struct ifnet *ifp; register struct mbuf *m; struct sockaddr *dst; struct rtentry *rtp; { register struct sl_softc *sc = ifp->if_softc; register struct ip *ip; - register struct ifqueue *ifq; - int s; + int s, error; /* * `Cannot happen' (see slioctl). Someday we will extend * the line protocol to support other address families. */ if (dst->sa_family != AF_INET) { if_printf(ifp, "af%d not supported\n", dst->sa_family); m_freem(m); sc->sc_if.if_noproto++; return (EAFNOSUPPORT); } if (sc->sc_ttyp == NULL || !(ifp->if_flags & IFF_UP)) { m_freem(m); return (ENETDOWN); } if ((sc->sc_ttyp->t_state & TS_CONNECTED) == 0) { m_freem(m); return (EHOSTUNREACH); } - ifq = (struct ifqueue *)&sc->sc_if.if_snd; ip = mtod(m, struct ip *); if (sc->sc_if.if_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) { m_freem(m); return (ENETRESET); /* XXX ? */ } - if (ip->ip_tos & IPTOS_LOWDELAY) - ifq = &sc->sc_fastq; - if (! IF_HANDOFF(ifq, m, NULL)) { + if (ip->ip_tos & IPTOS_LOWDELAY && + !ALTQ_IS_ENABLED(&sc->sc_if.if_snd)) + error = !(IF_HANDOFF(&sc->sc_fastq, m, NULL)); + else + IFQ_HANDOFF(&sc->sc_if, m, error); + if (error) { sc->sc_if.if_oerrors++; return (ENOBUFS); } s = splimp(); if (sc->sc_ttyp->t_outq.c_cc == 0) slstart(sc->sc_ttyp); splx(s); return (0); } /* * Start output on interface. Get another datagram * to send from the interface queue and map it to * the interface before starting output. */ static int slstart(tp) register struct tty *tp; { register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; register struct mbuf *m; register u_char *cp; register struct ip *ip; int s; register int len = 0; for (;;) { /* * Call output process whether or not there is more in the * output queue. We are being called in lieu of ttstart * and must do what it would. */ (*tp->t_oproc)(tp); if (tp->t_outq.c_cc != 0) { if (sc != NULL) sc->sc_flags &= ~SC_OUTWAIT; if (tp->t_outq.c_cc > SLIP_HIWAT) return 0; } /* * This happens briefly when the line shuts down. */ if (sc == NULL) return 0; /* * Get a packet and send it to the interface. */ s = splimp(); IF_DEQUEUE(&sc->sc_fastq, m); if (m) sc->sc_if.if_omcasts++; /* XXX */ else IF_DEQUEUE(&sc->sc_if.if_snd, m); splx(s); if (m == NULL) return 0; /* * We do the header compression here rather than in sloutput * because the packets will be out of order if we are using TOS * queueing, and the connection id compression will get * munged when this happens. */ if (sc->sc_if.if_bpf) { /* * We need to save the TCP/IP header before it's * compressed. To avoid complicated code, we just * copy the entire packet into a stack buffer (since * this is a serial line, packets should be short * and/or the copy should be negligible cost compared * to the packet transmission time). */ register struct mbuf *m1 = m; register u_char *cp; if (sc->bpfbuf == NULL) MALLOC(sc->bpfbuf, u_char *, SLTMAX + SLIP_HDRLEN, M_SL, M_NOWAIT); if (sc->bpfbuf) { cp = sc->bpfbuf + SLIP_HDRLEN; len = 0; do { register int mlen = m1->m_len; bcopy(mtod(m1, caddr_t), cp, mlen); cp += mlen; len += mlen; } while ((m1 = m1->m_next) != NULL); } } ip = mtod(m, struct ip *); if (ip->ip_v == IPVERSION && ip->ip_p == IPPROTO_TCP) { if (sc->sc_if.if_flags & SC_COMPRESS) *mtod(m, u_char *) |= sl_compress_tcp(m, ip, &sc->sc_comp, 1); } if (sc->sc_if.if_bpf && sc->bpfbuf) { /* * Put the SLIP pseudo-"link header" in place. The * compressed header is now at the beginning of the * mbuf. */ sc->bpfbuf[SLX_DIR] = SLIPDIR_OUT; bcopy(mtod(m, caddr_t), &sc->bpfbuf[SLX_CHDR], CHDR_LEN); BPF_TAP(&sc->sc_if, sc->bpfbuf, len + SLIP_HDRLEN); } /* * If system is getting low on clists, just flush our * output queue (if the stuff was important, it'll get * retransmitted). Note that SLTMAX is used instead of * the current if_mtu setting because connections that * have already been established still use the original * (possibly larger) mss. */ if (cfreecount < CLISTRESERVE + SLTMAX) { m_freem(m); sc->sc_if.if_collisions++; continue; } sc->sc_flags &= ~SC_OUTWAIT; /* * The extra FRAME_END will start up a new packet, and thus * will flush any accumulated garbage. We do this whenever * the line may have been idle for some time. */ if (tp->t_outq.c_cc == 0) { ++sc->sc_if.if_obytes; (void) putc(FRAME_END, &tp->t_outq); } while (m) { register u_char *ep; cp = mtod(m, u_char *); ep = cp + m->m_len; while (cp < ep) { /* * Find out how many bytes in the string we can * handle without doing something special. */ register u_char *bp = cp; while (cp < ep) { switch (*cp++) { case FRAME_ESCAPE: case FRAME_END: --cp; goto out; } } out: if (cp > bp) { /* * Put n characters at once * into the tty output queue. */ if (b_to_q((char *)bp, cp - bp, &tp->t_outq)) break; sc->sc_if.if_obytes += cp - bp; } /* * If there are characters left in the mbuf, * the first one must be special.. * Put it out in a different form. */ if (cp < ep) { if (putc(FRAME_ESCAPE, &tp->t_outq)) break; if (putc(*cp++ == FRAME_ESCAPE ? TRANS_FRAME_ESCAPE : TRANS_FRAME_END, &tp->t_outq)) { (void) unputc(&tp->t_outq); break; } sc->sc_if.if_obytes += 2; } } m = m_free(m); } if (putc(FRAME_END, &tp->t_outq)) { /* * Not enough room. Remove a char to make room * and end the packet normally. * If you get many collisions (more than one or two * a day) you probably do not have enough clists * and you should increase "nclist" in param.c. */ (void) unputc(&tp->t_outq); (void) putc(FRAME_END, &tp->t_outq); sc->sc_if.if_collisions++; } else { ++sc->sc_if.if_obytes; sc->sc_if.if_opackets++; } } return 0; } /* * Copy data buffer to mbuf chain; add ifnet pointer. */ static struct mbuf * sl_btom(sc, len) register struct sl_softc *sc; register int len; { struct mbuf *m, *newm; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return (NULL); /* * If we have more than MHLEN bytes, it's cheaper to * queue the cluster we just filled & allocate a new one * for the input buffer. Otherwise, fill the mbuf we * allocated above. Note that code in the input routine * guarantees that packet will fit in a cluster. */ if (len >= MHLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { /* * we couldn't get a cluster - if memory's this * low, it's time to start dropping packets. */ (void) m_free(m); return (NULL); } /* Swap the new and old clusters */ newm = m; m = sc->sc_mbuf; sc->sc_mbuf = newm; sc->sc_ep = mtod(newm, u_char *) + SLBUFSIZE; m->m_data = (caddr_t)sc->sc_buf; } else bcopy((caddr_t)sc->sc_buf, mtod(m, caddr_t), len); m->m_len = len; m->m_pkthdr.len = len; m->m_pkthdr.rcvif = &sc->sc_if; return (m); } /* * tty interface receiver interrupt. */ static int slinput(c, tp) register int c; register struct tty *tp; { register struct sl_softc *sc; register struct mbuf *m; register int len; u_char chdr[CHDR_LEN]; tk_nin++; sc = (struct sl_softc *)tp->t_sc; if (sc == NULL) return 0; if (c & TTY_ERRORMASK || (tp->t_state & TS_CONNECTED) == 0) { sc->sc_flags |= SC_ERROR; return 0; } c &= TTY_CHARMASK; ++sc->sc_if.if_ibytes; if (sc->sc_if.if_flags & IFF_DEBUG) { if (c == ABT_ESC) { /* * If we have a previous abort, see whether * this one is within the time limit. */ if (sc->sc_abortcount && time_second >= sc->sc_starttime + ABT_WINDOW) sc->sc_abortcount = 0; /* * If we see an abort after "idle" time, count it; * record when the first abort escape arrived. */ if (time_second >= sc->sc_lasttime + ABT_IDLE) { if (++sc->sc_abortcount == 1) sc->sc_starttime = time_second; if (sc->sc_abortcount >= ABT_COUNT) { slclose(tp,0); return 0; } } } else sc->sc_abortcount = 0; sc->sc_lasttime = time_second; } switch (c) { case TRANS_FRAME_ESCAPE: if (sc->sc_escape) c = FRAME_ESCAPE; break; case TRANS_FRAME_END: if (sc->sc_escape) c = FRAME_END; break; case FRAME_ESCAPE: sc->sc_escape = 1; return 0; case FRAME_END: sc->sc_flags &= ~SC_KEEPALIVE; if(sc->sc_flags & SC_ERROR) { sc->sc_flags &= ~SC_ERROR; goto newpack; } len = sc->sc_mp - sc->sc_buf; if (len < 3) /* less than min length packet - ignore */ goto newpack; if (sc->sc_if.if_bpf) { /* * Save the compressed header, so we * can tack it on later. Note that we * will end up copying garbage in some * cases but this is okay. We remember * where the buffer started so we can * compute the new header length. */ bcopy(sc->sc_buf, chdr, CHDR_LEN); } if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) { if (c & 0x80) c = TYPE_COMPRESSED_TCP; else if (c == TYPE_UNCOMPRESSED_TCP) *sc->sc_buf &= 0x4f; /* XXX */ /* * We've got something that's not an IP packet. * If compression is enabled, try to decompress it. * Otherwise, if `auto-enable' compression is on and * it's a reasonable packet, decompress it and then * enable compression. Otherwise, drop it. */ if (sc->sc_if.if_flags & SC_COMPRESS) { len = sl_uncompress_tcp(&sc->sc_buf, len, (u_int)c, &sc->sc_comp); if (len <= 0) goto error; } else if ((sc->sc_if.if_flags & SC_AUTOCOMP) && c == TYPE_UNCOMPRESSED_TCP && len >= 40) { len = sl_uncompress_tcp(&sc->sc_buf, len, (u_int)c, &sc->sc_comp); if (len <= 0) goto error; sc->sc_if.if_flags |= SC_COMPRESS; } else goto error; } if (sc->sc_if.if_bpf) { /* * Put the SLIP pseudo-"link header" in place. * We couldn't do this any earlier since * decompression probably moved the buffer * pointer. Then, invoke BPF. */ register u_char *hp = sc->sc_buf - SLIP_HDRLEN; hp[SLX_DIR] = SLIPDIR_IN; bcopy(chdr, &hp[SLX_CHDR], CHDR_LEN); BPF_TAP(&sc->sc_if, hp, len + SLIP_HDRLEN); } m = sl_btom(sc, len); if (m == NULL) goto error; sc->sc_if.if_ipackets++; if ((sc->sc_if.if_flags & IFF_UP) == 0) { m_freem(m); goto newpack; } if (! netisr_queue(NETISR_IP, m)) { sc->sc_if.if_ierrors++; sc->sc_if.if_iqdrops++; } goto newpack; } if (sc->sc_mp < sc->sc_ep) { *sc->sc_mp++ = c; sc->sc_escape = 0; return 0; } /* can't put lower; would miss an extra frame */ sc->sc_flags |= SC_ERROR; error: sc->sc_if.if_ierrors++; newpack: sc->sc_mp = sc->sc_buf = sc->sc_ep - SLRMAX; sc->sc_escape = 0; return 0; } /* * Process an ioctl request. */ static int slioctl(ifp, cmd, data) register struct ifnet *ifp; u_long cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *)data; register struct ifreq *ifr = (struct ifreq *)data; register int s, error = 0; struct sl_softc *sc = ifp->if_softc; s = splimp(); switch (cmd) { case SIOCSIFFLAGS: /* * if.c will set the interface up even if we * don't want it to. */ if (sc->sc_ttyp == NULL) { ifp->if_flags &= ~IFF_UP; } break; case SIOCSIFADDR: /* * This is "historical" - set the interface up when * setting the address. */ if (ifa->ifa_addr->sa_family == AF_INET) { if (sc->sc_ttyp != NULL) ifp->if_flags |= IFF_UP; } else { error = EAFNOSUPPORT; } break; case SIOCSIFDSTADDR: if (ifa->ifa_addr->sa_family != AF_INET) error = EAFNOSUPPORT; break; case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > SLTMAX) error = EINVAL; else { struct tty *tp; ifp->if_mtu = ifr->ifr_mtu; tp = sc->sc_ttyp; if (tp != NULL) clist_alloc_cblocks(&tp->t_outq, SLIP_HIWAT + 2 * ifp->if_mtu + 1, SLIP_HIWAT + 2 * ifp->if_mtu + 1); } break; default: error = EINVAL; } splx(s); return (error); } static void sl_keepalive(chan) void *chan; { struct sl_softc *sc = chan; if (sc->sc_keepalive) { if (sc->sc_flags & SC_KEEPALIVE) { if (sc->sc_ttyp->t_pgrp != NULL) { PGRP_LOCK(sc->sc_ttyp->t_pgrp); pgsignal (sc->sc_ttyp->t_pgrp, SIGURG, 1); PGRP_UNLOCK(sc->sc_ttyp->t_pgrp); } } else sc->sc_flags |= SC_KEEPALIVE; sc->sc_kahandle = timeout(sl_keepalive, sc, sc->sc_keepalive); } else { sc->sc_flags &= ~SC_KEEPALIVE; } } static void sl_outfill(chan) void *chan; { struct sl_softc *sc = chan; register struct tty *tp = sc->sc_ttyp; int s; if (sc->sc_outfill && tp != NULL) { if (sc->sc_flags & SC_OUTWAIT) { s = splimp (); ++sc->sc_if.if_obytes; (void) putc(FRAME_END, &tp->t_outq); (*tp->t_oproc)(tp); splx (s); } else sc->sc_flags |= SC_OUTWAIT; sc->sc_ofhandle = timeout(sl_outfill, sc, sc->sc_outfill); } else { sc->sc_flags &= ~SC_OUTWAIT; } } Index: head/sys/net/if_spppsubr.c =================================================================== --- head/sys/net/if_spppsubr.c (revision 130548) +++ head/sys/net/if_spppsubr.c (revision 130549) @@ -1,5326 +1,5331 @@ /* * 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, 2001 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 #include #include #include #include #if defined (__NetBSD__) || defined (__OpenBSD__) #include /* XXX for softnet */ #endif #include #include #include #include #ifdef INET #include #include #endif #if defined (__FreeBSD__) || defined (__OpenBSD__) # include #else # include #endif #ifdef IPX #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_VJ_COMP 0x002d /* VJ compressed TCP/IP */ #define PPP_VJ_UCOMP 0x002f /* VJ uncompressed TCP/IP */ #define PPP_IPV6 0x0057 /* Internet Protocol Version 6 */ #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 PPP_IPV6CP 0x8057 /* IPv6 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 IPV6CP_OPT_IFID 1 /* interface identifier */ #define IPV6CP_OPT_COMPRESSION 2 /* IPv6 compression protocol */ #define IPCP_COMP_VJ 0x2d /* Code for VJ compression */ #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; } __packed; #define PPP_HEADER_LEN sizeof (struct ppp_header) struct lcp_header { u_char type; u_char ident; u_short len; } __packed; #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; } __packed; #define CISCO_PACKET_LEN sizeof (struct cisco_packet) /* * 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 && __FreeBSD_version < 501113 #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 #ifdef INET /* * 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 const u_short interactive_ports[8] = { 0, 513, 0, 0, 0, 21, 0, 23, }; #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) #endif /* 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_ipv6cp_init(struct sppp *sp); static void sppp_ipv6cp_up(struct sppp *sp); static void sppp_ipv6cp_down(struct sppp *sp); static void sppp_ipv6cp_open(struct sppp *sp); static void sppp_ipv6cp_close(struct sppp *sp); static void sppp_ipv6cp_TO(void *sp); static int sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len); static void sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len); static void sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len); static void sppp_ipv6cp_tlu(struct sppp *sp); static void sppp_ipv6cp_tld(struct sppp *sp); static void sppp_ipv6cp_tls(struct sppp *sp); static void sppp_ipv6cp_tlf(struct sppp *sp); static void sppp_ipv6cp_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); #ifdef INET6 static const char *sppp_ipv6cp_opt_name(u_char opt); #endif 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); #ifdef INET6 static void sppp_get_ip6_addrs(struct sppp *sp, struct in6_addr *src, struct in6_addr *dst, struct in6_addr *srcmask); #ifdef IPV6CP_MYIFID_DYN static void sppp_set_ip6_addr(struct sppp *sp, const struct in6_addr *src); static void sppp_gen_ip6_addr(struct sppp *sp, const struct in6_addr *src); #endif static void sppp_suggest_ip6_addr(struct sppp *sp, struct in6_addr *src); #endif /* 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, #ifdef INET /* don't run IPCP if there's no IPv4 support */ CP_NCP, #else 0, #endif "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 ipv6cp = { PPP_IPV6CP, IDX_IPV6CP, #ifdef INET6 /*don't run IPv6CP if there's no IPv6 support*/ CP_NCP, #else 0, #endif "ipv6cp", sppp_ipv6cp_up, sppp_ipv6cp_down, sppp_ipv6cp_open, sppp_ipv6cp_close, sppp_ipv6cp_TO, sppp_ipv6cp_RCR, sppp_ipv6cp_RCN_rej, sppp_ipv6cp_RCN_nak, sppp_ipv6cp_tlu, sppp_ipv6cp_tld, sppp_ipv6cp_tls, sppp_ipv6cp_tlf, sppp_ipv6cp_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 */ &ipv6cp, /* IDX_IPV6CP */ &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; int isr = -1; struct sppp *sp = (struct sppp *)ifp; u_char *iphdr; int hlen, vjlen, do_account = 0; 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: m_freem (m); drop2: ++ifp->if_ierrors; ++ifp->if_iqdrops; 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[IDX_LCP], 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) { isr = NETISR_IP; } do_account++; break; case PPP_VJ_COMP: if (sp->state[IDX_IPCP] == STATE_OPENED) { if ((vjlen = sl_uncompress_tcp_core(mtod(m, u_char *), m->m_len, m->m_len, TYPE_COMPRESSED_TCP, sp->pp_comp, &iphdr, &hlen)) <= 0) { if (debug) log(LOG_INFO, SPP_FMT "VJ uncompress failed on compressed packet\n", SPP_ARGS(ifp)); goto drop; } /* * Trim the VJ header off the packet, and prepend * the uncompressed IP header (which will usually * end up in two chained mbufs since there's not * enough leading space in the existing mbuf). */ m_adj(m, vjlen); M_PREPEND(m, hlen, M_DONTWAIT); if (m == NULL) goto drop2; bcopy(iphdr, mtod(m, u_char *), hlen); isr = NETISR_IP; } do_account++; break; case PPP_VJ_UCOMP: if (sp->state[IDX_IPCP] == STATE_OPENED) { if (sl_uncompress_tcp_core(mtod(m, u_char *), m->m_len, m->m_len, TYPE_UNCOMPRESSED_TCP, sp->pp_comp, &iphdr, &hlen) != 0) { if (debug) log(LOG_INFO, SPP_FMT "VJ uncompress failed on uncompressed packet\n", SPP_ARGS(ifp)); goto drop; } isr = NETISR_IP; } do_account++; break; #endif #ifdef INET6 case PPP_IPV6CP: if (sp->pp_phase == PHASE_NETWORK) sppp_cp_input(&ipv6cp, sp, m); m_freem (m); return; case PPP_IPV6: if (sp->state[IDX_IPV6CP] == STATE_OPENED) isr = NETISR_IPV6; do_account++; break; #endif #ifdef IPX case PPP_IPX: /* IPX IPXCP not implemented yet */ if (sp->pp_phase == PHASE_NETWORK) isr = NETISR_IPX; do_account++; 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: isr = NETISR_IP; do_account++; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; do_account++; break; #endif #ifdef IPX case ETHERTYPE_IPX: isr = NETISR_IPX; do_account++; 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) || isr == -1) goto drop; /* Check queue. */ if (! netisr_queue(isr, m)) { if (debug) log(LOG_DEBUG, SPP_FMT "protocol queue overflow\n", SPP_ARGS(ifp)); goto drop; } if (do_account) /* * Do only account for network packets, not for control * packets. This is used by some subsystems to detect * idle lines. */ sp->pp_last_recv = time_second; } /* * 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 = NULL; - int s, rv = 0; + int s, error, rv = 0; int ipproto = PPP_IP; int debug = ifp->if_flags & IFF_DEBUG; s = splimp(); if ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == 0) { #ifdef INET6 drop: #endif m_freem (m); splx (s); return (ENETDOWN); } if ((ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == IFF_AUTO) { #ifdef INET6 /* * XXX * * Hack to prevent the initialization-time generated * IPv6 multicast packet to erroneously cause a * dialout event in case IPv6 has been * administratively disabled on that interface. */ if (dst->sa_family == AF_INET6 && !(sp->confflags & CONF_ENABLE_IPV6)) goto drop; #endif /* * 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 = (struct ifqueue *)&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. + * in front of the queue or let ALTQ take care. */ - if (_IF_QFULL(&sp->pp_fastq)) + if (ALTQ_IS_ENABLED(&ifp->if_snd)) ; + else 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; /* * Do IP Header compression */ if (sp->pp_mode != IFF_CISCO && (sp->ipcp.flags & IPCP_VJ) && ip->ip_p == IPPROTO_TCP) switch (sl_compress_tcp(m, ip, sp->pp_comp, sp->ipcp.compress_cid)) { case TYPE_COMPRESSED_TCP: ipproto = PPP_VJ_COMP; break; case TYPE_UNCOMPRESSED_TCP: ipproto = PPP_VJ_UCOMP; break; case TYPE_IP: ipproto = PPP_IP; break; default: m_freem(m); splx(s); return (EINVAL); } } #endif #ifdef INET6 if (dst->sa_family == AF_INET6) { /* XXX do something tricky here? */ } #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(ipproto); 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 { /* * 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_IPV6); if (sp->state[IDX_IPV6CP] != STATE_OPENED) rv = ENETDOWN; } break; #endif #ifdef IPX case AF_IPX: /* Novell IPX Protocol */ h->protocol = htons (sp->pp_mode == IFF_CISCO ? ETHERTYPE_IPX : PPP_IPX); break; #endif default: m_freem (m); ++ifp->if_oerrors; splx (s); return (EAFNOSUPPORT); } /* * Queue message on interface, and start output if interface * not yet active. */ - if (! IF_HANDOFF_ADJ(ifq, m, ifp, 3)) { + if (ifq != NULL) + error = !(IF_HANDOFF_ADJ(ifq, m, ifp, 3)); + else + IFQ_HANDOFF_ADJ(ifp, m, 3, error); + if (error) { ++ifp->if_oerrors; splx (s); return (rv? rv: ENOBUFS); } splx (s); /* * Unlike in sppp_input(), we can always bump the timestamp * here since sppp_output() is only called on behalf of * network-layer traffic; control-layer traffic is handled * by sppp_cp_send(). */ sp->pp_last_sent = time_second; return (0); } void sppp_attach(struct ifnet *ifp) { struct sppp *sp = (struct sppp*) ifp; /* Initialize keepalive handler. */ if (spppq == NULL) 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; bzero(&sp->pp_seq[0], sizeof(sp->pp_seq)); bzero(&sp->pp_rseq[0], sizeof(sp->pp_rseq)); sp->pp_phase = PHASE_DEAD; sp->pp_up = lcp.Up; sp->pp_down = lcp.Down; if(!mtx_initialized(&sp->pp_cpq.ifq_mtx)) mtx_init(&sp->pp_cpq.ifq_mtx, "sppp_cpq", NULL, MTX_DEF); if(!mtx_initialized(&sp->pp_fastq.ifq_mtx)) mtx_init(&sp->pp_fastq.ifq_mtx, "sppp_fastq", NULL, MTX_DEF); sp->pp_last_recv = sp->pp_last_sent = time_second; sp->confflags = 0; #ifdef INET sp->confflags |= CONF_ENABLE_VJ; #endif #ifdef INET6 sp->confflags |= CONF_ENABLE_IPV6; #endif sp->pp_comp = malloc(sizeof(struct slcompress), M_TEMP, M_WAITOK); sl_compress_init(sp->pp_comp, -1); sppp_lcp_init(sp); sppp_ipcp_init(sp); sppp_ipv6cp_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 == NULL) 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 ((struct ifqueue *)&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: /* set the interface "up" when assigning an IP address */ ifp->if_flags |= IFF_UP; /* FALLTHROUGH */ 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[IDX_LCP] = ntohl (h->par1); if (sp->pp_seq[IDX_LCP] == sp->pp_rseq[IDX_LCP]) { /* 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[IDX_LCP] = random(); #else sp->pp_seq[IDX_LCP] ^= 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; struct timeval tv; getmicrouptime(&tv); 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; ch->time0 = htons ((u_short) (tv.tv_sec >> 16)); ch->time1 = htons ((u_short) tv.tv_sec); 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); /* FALLTHROUGH */ 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); /* FALLTHROUGH */ 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); /* FALLTHROUGH */ 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); /* FALLTHROUGH */ 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: /* 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 PROTO_REJ: { int catastrophic; const struct cp *upper; int i; u_int16_t proto; catastrophic = 0; upper = NULL; proto = ntohs(*((u_int16_t *)p)); for (i = 0; i < IDX_COUNT; i++) { if (cps[i]->proto == proto) { upper = cps[i]; break; } } if (upper == NULL) catastrophic++; if (catastrophic || debug) log(catastrophic? LOG_INFO: LOG_DEBUG, SPP_FMT "%s: RXJ%c (%s) for proto 0x%x (%s/%s)\n", SPP_ARGS(ifp), cp->name, catastrophic ? '-' : '+', sppp_cp_type_name(h->type), proto, upper ? upper->name : "unknown", upper ? sppp_state_name(sp->state[upper->protoidx]) : "?"); /* * if we got RXJ+ against conf-req, the peer does not implement * this particular protocol type. terminate the protocol. */ if (upper && !catastrophic) { if (sp->state[upper->protoidx] == STATE_REQ_SENT) { upper->Close(sp); break; } } /* XXX catastrophic rejects (RXJ-) aren't handled yet. */ 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[cp->protoidx], 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: /* * Try escaping stopped state. This seems to bite * people occasionally, in particular for IPCP, * presumably following previous IPCP negotiation * aborts. Somehow, we must have missed a Down event * which would have caused a transition into starting * state, so as a bandaid we force the Down event now. * This effectively implements (something like the) * `restart' option mentioned in the state transition * table of RFC 1661. */ 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: 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); /* FALLTHROUGH */ 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[cp->protoidx], 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[cp->protoidx], 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. */ static 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->pp_seq[IDX_LCP] = 0; sp->pp_rseq[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 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; /* * 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"); } else if ((ifp->if_flags & (IFF_AUTO | IFF_PASSIVE)) == 0 && (sp->state[IDX_LCP] == STATE_INITIAL)) { ifp->if_flags |= IFF_RUNNING; lcp.Open(sp); } 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) { 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 -- just ignore it. * * Quote from RFC 1662, chapter 6: * To enable this functionality, synchronous PPP * implementations MUST always respond to the * Async-Control-Character-Map Configuration * Option with the LCP Configure-Ack. However, * acceptance of the Configuration Option does * not imply that the synchronous implementation * will do any ACCM mapping. Instead, all such * octet mapping will be performed by the * asynchronous-to-synchronous converter. */ continue; 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) && /* * XXX * Hack to administratively disable IPv6 if * not desired. Perhaps we should have another * flag for this, but right now, we can make * all struct cp's read/only. */ (cps[i] != &ipv6cp || (sp->confflags & CONF_ENABLE_IPV6))) (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[IDX_LCP]; 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; sp->pp_seq[IDX_IPCP] = 0; sp->pp_rseq[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 | IPCP_VJ); sp->ipcp.opts = 0; 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; if (sp->confflags & CONF_ENABLE_VJ) { sp->ipcp.opts |= (1 << IPCP_OPT_COMPRESSION); sp->ipcp.max_state = MAX_STATES - 1; sp->ipcp.compress_cid = 1; } 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; int desiredcomp; 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_COMPRESSION: if (!(sp->confflags & CONF_ENABLE_VJ)) { /* VJ compression administratively disabled */ if (debug) log(-1, "[locally disabled] "); break; } /* * In theory, we should only conf-rej an * option that is shorter than RFC 1618 * requires (i.e. < 4), and should conf-nak * anything else that is not VJ. However, * since our algorithm always uses the * original option to NAK it with new values, * things would become more complicated. In * pratice, the only commonly implemented IP * compression option is VJ anyway, so the * difference is negligible. */ if (len >= 6 && p[1] == 6) { /* * correctly formed compression option * that could be VJ compression */ continue; } if (debug) log(-1, "optlen %d [invalid/unsupported] ", p[1]); break; 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_COMPRESSION: desiredcomp = p[2] << 8 | p[3]; /* We only support VJ */ if (desiredcomp == IPCP_COMP_VJ) { if (debug) log(-1, "VJ [ack] "); sp->ipcp.flags |= IPCP_VJ; sl_compress_init(sp->pp_comp, p[4]); sp->ipcp.max_state = p[4]; sp->ipcp.compress_cid = p[5]; continue; } if (debug) log(-1, "compproto %#04x [not supported] ", desiredcomp); p[2] = IPCP_COMP_VJ >> 8; p[3] = IPCP_COMP_VJ; p[4] = sp->ipcp.max_state; p[5] = sp->ipcp.compress_cid; break; 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 && hisaddr <= 254 && desiredaddr != 0)) { /* * Peer's address is same as our value, * or we have set it to 0.0.0.* 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_COMPRESSION: sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESSION); break; 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; int desiredcomp; 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_COMPRESSION: if (len >= 6 && p[1] == 6) { desiredcomp = p[2] << 8 | p[3]; if (debug) log(-1, "[wantcomp %#04x] ", desiredcomp); if (desiredcomp == IPCP_COMP_VJ) { sl_compress_init(sp->pp_comp, p[4]); sp->ipcp.max_state = p[4]; sp->ipcp.compress_cid = p[5]; if (debug) log(-1, "[agree] "); } else sp->ipcp.opts &= ~(1 << IPCP_OPT_COMPRESSION); } break; 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_COMPRESSION)) { opt[i++] = IPCP_OPT_COMPRESSION; opt[i++] = 6; opt[i++] = IPCP_COMP_VJ >> 8; opt[i++] = IPCP_COMP_VJ; opt[i++] = sp->ipcp.max_state; opt[i++] = sp->ipcp.compress_cid; } 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[IDX_IPCP]; sppp_cp_send(sp, PPP_IPCP, CONF_REQ, sp->confid[IDX_IPCP], i, &opt); } /* *--------------------------------------------------------------------------* * * * The IPv6CP implementation. * * * *--------------------------------------------------------------------------* */ #ifdef INET6 static void sppp_ipv6cp_init(struct sppp *sp) { sp->ipv6cp.opts = 0; sp->ipv6cp.flags = 0; sp->state[IDX_IPV6CP] = STATE_INITIAL; sp->fail_counter[IDX_IPV6CP] = 0; sp->pp_seq[IDX_IPV6CP] = 0; sp->pp_rseq[IDX_IPV6CP] = 0; #if defined(__NetBSD__) callout_init(&sp->ch[IDX_IPV6CP]); #endif #if defined(__FreeBSD__) && __FreeBSD__ >= 3 callout_handle_init(&sp->ch[IDX_IPV6CP]); #endif } static void sppp_ipv6cp_up(struct sppp *sp) { sppp_up_event(&ipv6cp, sp); } static void sppp_ipv6cp_down(struct sppp *sp) { sppp_down_event(&ipv6cp, sp); } static void sppp_ipv6cp_open(struct sppp *sp) { STDDCL; struct in6_addr myaddr, hisaddr; #ifdef IPV6CP_MYIFID_DYN sp->ipv6cp.flags &= ~(IPV6CP_MYIFID_SEEN|IPV6CP_MYIFID_DYN); #else sp->ipv6cp.flags &= ~IPV6CP_MYIFID_SEEN; #endif sppp_get_ip6_addrs(sp, &myaddr, &hisaddr, 0); /* * If we don't have our address, this probably means our * interface doesn't want to talk IPv6 at all. (This could * be the case if somebody wants to speak only IPX, for * example.) Don't open IPv6CP in this case. */ if (IN6_IS_ADDR_UNSPECIFIED(&myaddr)) { /* XXX this message should go away */ if (debug) log(LOG_DEBUG, SPP_FMT "ipv6cp_open(): no IPv6 interface\n", SPP_ARGS(ifp)); return; } sp->ipv6cp.flags |= IPV6CP_MYIFID_SEEN; sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID); sppp_open_event(&ipv6cp, sp); } static void sppp_ipv6cp_close(struct sppp *sp) { sppp_close_event(&ipv6cp, sp); } static void sppp_ipv6cp_TO(void *cookie) { sppp_to_event(&ipv6cp, (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_ipv6cp_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; struct in6_addr myaddr, desiredaddr, suggestaddr; int ifidcount; int type; int collision, nohisaddr; 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 "ipv6cp parse opts:", SPP_ARGS(ifp)); p = (void*) (h+1); ifidcount = 0; for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { if (debug) log(-1, " %s", sppp_ipv6cp_opt_name(*p)); switch (*p) { case IPV6CP_OPT_IFID: if (len >= 10 && p[1] == 10 && ifidcount == 0) { /* correctly formed address option */ ifidcount++; continue; } if (debug) log(-1, " [invalid]"); break; #ifdef notyet case IPV6CP_OPT_COMPRESSION: if (len >= 4 && p[1] >= 4) { /* correctly formed compress option */ continue; } if (debug) log(-1, " [invalid]"); break; #endif 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_IPV6CP, CONF_REJ, h->ident, rlen, buf); goto end; } else if (debug) log(-1, "\n"); /* pass 2: parse option values */ sppp_get_ip6_addrs(sp, &myaddr, 0, 0); if (debug) log(LOG_DEBUG, SPP_FMT "ipv6cp parse opt values: ", SPP_ARGS(ifp)); p = (void*) (h+1); len = origlen; type = CONF_ACK; for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) { if (debug) log(-1, " %s", sppp_ipv6cp_opt_name(*p)); switch (*p) { #ifdef notyet case IPV6CP_OPT_COMPRESSION: continue; #endif case IPV6CP_OPT_IFID: bzero(&desiredaddr, sizeof(desiredaddr)); bcopy(&p[2], &desiredaddr.s6_addr[8], 8); collision = (bcmp(&desiredaddr.s6_addr[8], &myaddr.s6_addr[8], 8) == 0); nohisaddr = IN6_IS_ADDR_UNSPECIFIED(&desiredaddr); desiredaddr.s6_addr16[0] = htons(0xfe80); desiredaddr.s6_addr16[1] = htons(sp->pp_if.if_index); if (!collision && !nohisaddr) { /* no collision, hisaddr known - Conf-Ack */ type = CONF_ACK; if (debug) { log(-1, " %s [%s]", ip6_sprintf(&desiredaddr), sppp_cp_type_name(type)); } continue; } bzero(&suggestaddr, sizeof(&suggestaddr)); if (collision && nohisaddr) { /* collision, hisaddr unknown - Conf-Rej */ type = CONF_REJ; bzero(&p[2], 8); } else { /* * - no collision, hisaddr unknown, or * - collision, hisaddr known * Conf-Nak, suggest hisaddr */ type = CONF_NAK; sppp_suggest_ip6_addr(sp, &suggestaddr); bcopy(&suggestaddr.s6_addr[8], &p[2], 8); } if (debug) log(-1, " %s [%s]", ip6_sprintf(&desiredaddr), sppp_cp_type_name(type)); break; } /* Add the option to nak'ed list. */ bcopy (p, r, p[1]); r += p[1]; rlen += p[1]; } if (rlen == 0 && type == CONF_ACK) { if (debug) log(-1, " send %s\n", sppp_cp_type_name(type)); sppp_cp_send (sp, PPP_IPV6CP, type, h->ident, origlen, h+1); } else { #ifdef DIAGNOSTIC if (type == CONF_ACK) panic("IPv6CP RCR: CONF_ACK with non-zero rlen"); #endif if (debug) { log(-1, " send %s suggest %s\n", sppp_cp_type_name(type), ip6_sprintf(&suggestaddr)); } sppp_cp_send (sp, PPP_IPV6CP, type, h->ident, rlen, buf); } end: free (buf, M_TEMP); return (rlen == 0); } /* * Analyze the IPv6CP Configure-Reject option list, and adjust our * negotiation. */ static void sppp_ipv6cp_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 "ipv6cp 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_ipv6cp_opt_name(*p)); switch (*p) { case IPV6CP_OPT_IFID: /* * Peer doesn't grok address option. This is * bad. XXX Should we better give up here? */ sp->ipv6cp.opts &= ~(1 << IPV6CP_OPT_IFID); break; #ifdef notyet case IPV6CP_OPT_COMPRESS: sp->ipv6cp.opts &= ~(1 << IPV6CP_OPT_COMPRESS); break; #endif } } if (debug) log(-1, "\n"); free (buf, M_TEMP); return; } /* * Analyze the IPv6CP Configure-NAK option list, and adjust our * negotiation. */ static void sppp_ipv6cp_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; struct in6_addr suggestaddr; len -= 4; buf = malloc (len, M_TEMP, M_NOWAIT); if (!buf) return; if (debug) log(LOG_DEBUG, SPP_FMT "ipv6cp 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_ipv6cp_opt_name(*p)); switch (*p) { case IPV6CP_OPT_IFID: /* * Peer doesn't like our local ifid. See * if we can do something for him. We'll drop * him our address then. */ if (len < 10 || p[1] != 10) break; bzero(&suggestaddr, sizeof(suggestaddr)); suggestaddr.s6_addr16[0] = htons(0xfe80); suggestaddr.s6_addr16[1] = htons(sp->pp_if.if_index); bcopy(&p[2], &suggestaddr.s6_addr[8], 8); sp->ipv6cp.opts |= (1 << IPV6CP_OPT_IFID); if (debug) log(-1, " [suggestaddr %s]", ip6_sprintf(&suggestaddr)); #ifdef IPV6CP_MYIFID_DYN /* * When doing dynamic address assignment, * we accept his offer. */ if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN) { struct in6_addr lastsuggest; /* * If equals to * , * we have a collision. generate new random * ifid. */ sppp_suggest_ip6_addr(&lastsuggest); if (IN6_ARE_ADDR_EQUAL(&suggestaddr, lastsuggest)) { if (debug) log(-1, " [random]"); sppp_gen_ip6_addr(sp, &suggestaddr); } sppp_set_ip6_addr(sp, &suggestaddr, 0); if (debug) log(-1, " [agree]"); sp->ipv6cp.flags |= IPV6CP_MYIFID_SEEN; } #else /* * Since we do not do dynamic address assignment, * we ignore it and thus continue to negotiate * our already existing value. This can possibly * go into infinite request-reject loop. * * This is not likely because we normally use * ifid based on MAC-address. * If you have no ethernet card on the node, too bad. * XXX should we use fail_counter? */ #endif break; #ifdef notyet case IPV6CP_OPT_COMPRESS: /* * Peer wants different compression parameters. */ break; #endif } } if (debug) log(-1, "\n"); free (buf, M_TEMP); return; } static void sppp_ipv6cp_tlu(struct sppp *sp) { /* we are up - notify isdn daemon */ if (sp->pp_con) sp->pp_con(sp); } static void sppp_ipv6cp_tld(struct sppp *sp) { } static void sppp_ipv6cp_tls(struct sppp *sp) { /* indicate to LCP that it must stay alive */ sp->lcp.protos |= (1 << IDX_IPV6CP); } static void sppp_ipv6cp_tlf(struct sppp *sp) { #if 0 /* need #if 0 to close IPv6CP properly */ /* we no longer need LCP */ sp->lcp.protos &= ~(1 << IDX_IPV6CP); sppp_lcp_check_and_close(sp); #endif } static void sppp_ipv6cp_scr(struct sppp *sp) { char opt[10 /* ifid */ + 4 /* compression, minimum */]; struct in6_addr ouraddr; int i = 0; if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_IFID)) { sppp_get_ip6_addrs(sp, &ouraddr, 0, 0); opt[i++] = IPV6CP_OPT_IFID; opt[i++] = 10; bcopy(&ouraddr.s6_addr[8], &opt[i], 8); i += 8; } #ifdef notyet if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_COMPRESSION)) { opt[i++] = IPV6CP_OPT_COMPRESSION; opt[i++] = 4; opt[i++] = 0; /* TBD */ opt[i++] = 0; /* TBD */ /* variable length data may follow */ } #endif sp->confid[IDX_IPV6CP] = ++sp->pp_seq[IDX_IPV6CP]; sppp_cp_send(sp, PPP_IPV6CP, CONF_REQ, sp->confid[IDX_IPV6CP], i, &opt); } #else /*INET6*/ static void sppp_ipv6cp_init(struct sppp *sp) { } static void sppp_ipv6cp_up(struct sppp *sp) { } static void sppp_ipv6cp_down(struct sppp *sp) { } static void sppp_ipv6cp_open(struct sppp *sp) { } static void sppp_ipv6cp_close(struct sppp *sp) { } static void sppp_ipv6cp_TO(void *sp) { } static int sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len) { return 0; } static void sppp_ipv6cp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len) { } static void sppp_ipv6cp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len) { } static void sppp_ipv6cp_tlu(struct sppp *sp) { } static void sppp_ipv6cp_tld(struct sppp *sp) { } static void sppp_ipv6cp_tls(struct sppp *sp) { } static void sppp_ipv6cp_tlf(struct sppp *sp) { } static void sppp_ipv6cp_scr(struct sppp *sp) { } #endif /*INET6*/ /* *--------------------------------------------------------------------------* * * * 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. */ static 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; sp->pp_seq[IDX_CHAP] = 0; sp->pp_rseq[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; /* FALLTHROUGH */ 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[IDX_CHAP]; 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 != sppp_strnlen(sp->hisauth.name, AUTHNAMELEN) || passwd_len != sppp_strnlen(sp->hisauth.secret, 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; sp->pp_seq[IDX_PAP] = 0; sp->pp_rseq[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[IDX_PAP]; 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[IDX_LCP], sp->pp_rseq[IDX_LCP]); else if (sp->pp_phase >= PHASE_AUTHENTICATE) { long nmagic = htonl (sp->lcp.magic); sp->lcp.echoid = ++sp->pp_seq[IDX_LCP]; 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 = TAILQ_FIRST(&ifp->if_addrlist); ifa; 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; struct in_ifaddr *ia; /* * 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 = TAILQ_FIRST(&ifp->if_addrlist); ifa; 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); ia = ifatoia(ifa); LIST_REMOVE(ia, ia_hash); LIST_INSERT_HEAD(INADDR_HASH(si->sin_addr.s_addr), ia, ia_hash); /* 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 } } #ifdef INET6 /* * Get both IPv6 addresses. */ static void sppp_get_ip6_addrs(struct sppp *sp, struct in6_addr *src, struct in6_addr *dst, struct in6_addr *srcmask) { struct ifnet *ifp = &sp->pp_if; struct ifaddr *ifa; struct sockaddr_in6 *si, *sm; struct in6_addr ssrc, ddst; sm = NULL; bzero(&ssrc, sizeof(ssrc)); bzero(&ddst, sizeof(ddst)); /* * Pick the first link-local AF_INET6 address from the list, * aliases don't make any sense on a p2p link anyway. */ #if defined(__FreeBSD__) && __FreeBSD__ >= 3 for (ifa = ifp->if_addrhead.tqh_first, si = 0; ifa; ifa = ifa->ifa_link.tqe_next) #elif defined(__NetBSD__) || defined (__OpenBSD__) for (ifa = ifp->if_addrlist.tqh_first, si = 0; ifa; ifa = ifa->ifa_list.tqe_next) #else for (ifa = ifp->if_addrlist, si = 0; ifa; ifa = ifa->ifa_next) #endif if (ifa->ifa_addr->sa_family == AF_INET6) { si = (struct sockaddr_in6 *)ifa->ifa_addr; sm = (struct sockaddr_in6 *)ifa->ifa_netmask; if (si && IN6_IS_ADDR_LINKLOCAL(&si->sin6_addr)) break; } if (ifa) { if (si && !IN6_IS_ADDR_UNSPECIFIED(&si->sin6_addr)) { bcopy(&si->sin6_addr, &ssrc, sizeof(ssrc)); if (srcmask) { bcopy(&sm->sin6_addr, srcmask, sizeof(*srcmask)); } } si = (struct sockaddr_in6 *)ifa->ifa_dstaddr; if (si && !IN6_IS_ADDR_UNSPECIFIED(&si->sin6_addr)) bcopy(&si->sin6_addr, &ddst, sizeof(ddst)); } if (dst) bcopy(&ddst, dst, sizeof(*dst)); if (src) bcopy(&ssrc, src, sizeof(*src)); } #ifdef IPV6CP_MYIFID_DYN /* * Generate random ifid. */ static void sppp_gen_ip6_addr(struct sppp *sp, struct in6_addr *addr) { /* TBD */ } /* * Set my IPv6 address. Must be called at splimp. */ static void sppp_set_ip6_addr(struct sppp *sp, const struct in6_addr *src) { STDDCL; struct ifaddr *ifa; struct sockaddr_in6 *sin6; /* * Pick the first link-local AF_INET6 address from the list, * aliases don't make any sense on a p2p link anyway. */ sin6 = NULL; #if defined(__FreeBSD__) && __FreeBSD__ >= 3 for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) #elif defined(__NetBSD__) || defined (__OpenBSD__) for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) #else for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) #endif { if (ifa->ifa_addr->sa_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; if (sin6 && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) break; } } if (ifa && sin6) { int error; struct sockaddr_in6 new_sin6 = *sin6; bcopy(src, &new_sin6.sin6_addr, sizeof(new_sin6.sin6_addr)); error = in6_ifinit(ifp, ifatoia6(ifa), &new_sin6, 1); if (debug && error) { log(LOG_DEBUG, SPP_FMT "sppp_set_ip6_addr: in6_ifinit " " failed, error=%d\n", SPP_ARGS(ifp), error); } } } #endif /* * Suggest a candidate address to be used by peer. */ static void sppp_suggest_ip6_addr(struct sppp *sp, struct in6_addr *suggest) { struct in6_addr myaddr; struct timeval tv; sppp_get_ip6_addrs(sp, &myaddr, 0, 0); myaddr.s6_addr[8] &= ~0x02; /* u bit to "local" */ microtime(&tv); if ((tv.tv_usec & 0xff) == 0 && (tv.tv_sec & 0xff) == 0) { myaddr.s6_addr[14] ^= 0xff; myaddr.s6_addr[15] ^= 0xff; } else { myaddr.s6_addr[14] ^= (tv.tv_usec & 0xff); myaddr.s6_addr[15] ^= (tv.tv_sec & 0xff); } if (suggest) bcopy(&myaddr, suggest, sizeof(myaddr)); } #endif /*INET6*/ static int sppp_params(struct sppp *sp, u_long cmd, void *data) { u_long subcmd; struct ifreq *ifr = (struct ifreq *)data; struct spppreq *spr; int rv = 0; if ((spr = malloc(sizeof(struct spppreq), M_TEMP, M_NOWAIT)) == 0) return (EAGAIN); /* * 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) { rv = EFAULT; goto quit; } if (copyin((caddr_t)ifr->ifr_data, spr, sizeof(struct spppreq)) != 0) { rv = EFAULT; goto quit; } switch (subcmd) { case (int)SPPPIOGDEFS: if (cmd != SIOCGIFGENERIC) { rv = EINVAL; break; } /* * 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. */ spr->defs.pp_phase = sp->pp_phase; spr->defs.enable_vj = (sp->confflags & CONF_ENABLE_VJ) != 0; spr->defs.enable_ipv6 = (sp->confflags & CONF_ENABLE_IPV6) != 0; spr->defs.lcp = sp->lcp; spr->defs.ipcp = sp->ipcp; spr->defs.ipv6cp = sp->ipv6cp; spr->defs.myauth = sp->myauth; spr->defs.hisauth = sp->hisauth; bzero(spr->defs.myauth.secret, AUTHKEYLEN); bzero(spr->defs.myauth.challenge, AUTHKEYLEN); bzero(spr->defs.hisauth.secret, AUTHKEYLEN); bzero(spr->defs.hisauth.challenge, AUTHKEYLEN); /* * Fixup the LCP timeout value to milliseconds so * spppcontrol doesn't need to bother about the value * of "hz". We do the reverse calculation below when * setting it. */ spr->defs.lcp.timeout = sp->lcp.timeout * 1000 / hz; rv = copyout(spr, (caddr_t)ifr->ifr_data, sizeof(struct spppreq)); break; case (int)SPPPIOSDEFS: if (cmd != SIOCSIFGENERIC) { rv = EINVAL; break; } /* * 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 or establish * phase. Once the authentication 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 && sp->pp_phase != PHASE_ESTABLISH) { rv = EBUSY; break; } 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)) { rv = EINVAL; break; } 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); } /* set LCP restart timer timeout */ if (spr->defs.lcp.timeout != 0) sp->lcp.timeout = spr->defs.lcp.timeout * hz / 1000; /* set VJ enable and IPv6 disable flags */ #ifdef INET if (spr->defs.enable_vj) sp->confflags |= CONF_ENABLE_VJ; else sp->confflags &= ~CONF_ENABLE_VJ; #endif #ifdef INET6 if (spr->defs.enable_ipv6) sp->confflags |= CONF_ENABLE_IPV6; else sp->confflags &= ~CONF_ENABLE_IPV6; #endif break; default: rv = EINVAL; } quit: free(spr, M_TEMP); return (rv); } 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; } #ifdef INET6 static const char * sppp_ipv6cp_opt_name(u_char opt) { static char buf[12]; switch (opt) { case IPV6CP_OPT_IFID: return "ifid"; case IPV6CP_OPT_COMPRESSION: return "compression"; } sprintf (buf, "0x%x", opt); return buf; } #endif 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"; case PPP_IPV6CP: return "ipv6cp"; } 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_tun.c =================================================================== --- head/sys/net/if_tun.c (revision 130548) +++ head/sys/net/if_tun.c (revision 130549) @@ -1,857 +1,856 @@ /* $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_atalk.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include "opt_mac.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 #ifdef INET #include #endif #include #include #include /* * tun_list is protected by global tunmtx. Other mutable fields are * protected by tun->tun_mtx, or by their owning subsystem. tun_dev is * static for the duration of a tunnel interface. */ struct tun_softc { TAILQ_ENTRY(tun_softc) tun_list; dev_t tun_dev; u_short tun_flags; /* misc flags */ #define TUN_OPEN 0x0001 #define TUN_INITED 0x0002 #define TUN_RCOLL 0x0004 #define TUN_IASET 0x0008 #define TUN_DSTADDR 0x0010 #define TUN_LMODE 0x0020 #define TUN_RWAIT 0x0040 #define TUN_ASYNC 0x0080 #define TUN_IFHEAD 0x0100 #define TUN_READY (TUN_OPEN | TUN_INITED) /* * XXXRW: tun_pid is used to exclusively lock /dev/tun. Is this * actually needed? Can we just return EBUSY if already open? * Problem is that this involved inherent races when a tun device * is handed off from one process to another, as opposed to just * being slightly stale informationally. */ pid_t tun_pid; /* owning pid */ struct ifnet tun_if; /* the interface */ struct sigio *tun_sigio; /* information for async I/O */ struct selinfo tun_rsel; /* read select */ struct mtx tun_mtx; /* protect mutable softc fields */ }; #define TUNDEBUG if (tundebug) if_printf #define TUNNAME "tun" /* * All mutable global variables in if_tun are locked using tunmtx, with * the exception of tundebug, which is used unlocked, and tunclones, * which is static after setup. */ static struct mtx tunmtx; static MALLOC_DEFINE(M_TUN, TUNNAME, "Tunnel Interface"); static int tundebug = 0; static struct clonedevs *tunclones; static TAILQ_HEAD(,tun_softc) tunhead = TAILQ_HEAD_INITIALIZER(tunhead); SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, ""); static void tunclone(void *arg, char *name, int namelen, dev_t *dev); static void tuncreate(dev_t dev); static int tunifioctl(struct ifnet *, u_long, caddr_t); static int tuninit(struct ifnet *); static int tunmodevent(module_t, int, void *); static int tunoutput(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *rt); static void tunstart(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; static struct cdevsw tun_cdevsw = { .d_version = D_VERSION, .d_flags = D_PSEUDO | D_NEEDGIANT, .d_open = tunopen, .d_close = tunclose, .d_read = tunread, .d_write = tunwrite, .d_ioctl = tunioctl, .d_poll = tunpoll, .d_name = TUNNAME, }; static void tunclone(void *arg, char *name, int namelen, dev_t *dev) { int u, i; if (*dev != NODEV) return; if (strcmp(name, TUNNAME) == 0) { u = -1; } else if (dev_stdclone(name, NULL, TUNNAME, &u) != 1) return; /* Don't recognise the name */ if (u != -1 && u > IF_MAXUNIT) return; /* Unit number too high */ /* find any existing device, or allocate new unit number */ i = clone_create(&tunclones, &tun_cdevsw, &u, dev, 0); if (i) { /* No preexisting dev_t, create one */ *dev = make_dev(&tun_cdevsw, unit2minor(u), UID_UUCP, GID_DIALER, 0600, "tun%d", u); if (*dev != NULL) (*dev)->si_flags |= SI_CHEAPCLONE; } } static void tun_destroy(struct tun_softc *tp) { dev_t dev; /* Unlocked read. */ KASSERT((tp->tun_flags & TUN_OPEN) == 0, ("tununits is out of sync - unit %d", tp->tun_if.if_dunit)); dev = tp->tun_dev; bpfdetach(&tp->tun_if); if_detach(&tp->tun_if); destroy_dev(dev); mtx_destroy(&tp->tun_mtx); free(tp, M_TUN); } static int tunmodevent(module_t mod, int type, void *data) { static eventhandler_tag tag; struct tun_softc *tp; switch (type) { case MOD_LOAD: mtx_init(&tunmtx, "tunmtx", NULL, MTX_DEF); clone_setup(&tunclones); tag = EVENTHANDLER_REGISTER(dev_clone, tunclone, 0, 1000); if (tag == NULL) return (ENOMEM); break; case MOD_UNLOAD: EVENTHANDLER_DEREGISTER(dev_clone, tag); mtx_lock(&tunmtx); while ((tp = TAILQ_FIRST(&tunhead)) != NULL) { TAILQ_REMOVE(&tunhead, tp, tun_list); mtx_unlock(&tunmtx); tun_destroy(tp); mtx_lock(&tunmtx); } mtx_unlock(&tunmtx); clone_cleanup(&tunclones); mtx_destroy(&tunmtx); break; } return 0; } static moduledata_t tun_mod = { "if_tun", tunmodevent, 0 }; DECLARE_MODULE(if_tun, tun_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); static void tunstart(struct ifnet *ifp) { struct tun_softc *tp = ifp->if_softc; mtx_lock(&tp->tun_mtx); if (tp->tun_flags & TUN_RWAIT) { tp->tun_flags &= ~TUN_RWAIT; wakeup(tp); } if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio) { mtx_unlock(&tp->tun_mtx); pgsigio(&tp->tun_sigio, SIGIO, 0); } else mtx_unlock(&tp->tun_mtx); selwakeuppri(&tp->tun_rsel, PZERO + 1); } static void tuncreate(dev_t dev) { struct tun_softc *sc; struct ifnet *ifp; dev->si_flags &= ~SI_CHEAPCLONE; MALLOC(sc, struct tun_softc *, sizeof(*sc), M_TUN, M_WAITOK | M_ZERO); mtx_init(&sc->tun_mtx, "tun_mtx", NULL, MTX_DEF); sc->tun_flags = TUN_INITED; sc->tun_dev = dev; mtx_lock(&tunmtx); TAILQ_INSERT_TAIL(&tunhead, sc, tun_list); mtx_unlock(&tunmtx); ifp = &sc->tun_if; if_initname(ifp, TUNNAME, dev2unit(dev)); 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; } static int tunopen(dev_t dev, int flag, int mode, struct thread *td) { struct ifnet *ifp; struct tun_softc *tp; /* * XXXRW: Non-atomic test and set of dev->si_drv1 requires * synchronization. */ tp = dev->si_drv1; if (!tp) { tuncreate(dev); tp = dev->si_drv1; } /* * XXXRW: This use of tun_pid is subject to error due to the * fact that a reference to the tunnel can live beyond the * death of the process that created it. Can we replace this * with a simple busy flag? */ mtx_lock(&tp->tun_mtx); if (tp->tun_pid != 0 && tp->tun_pid != td->td_proc->p_pid) { mtx_unlock(&tp->tun_mtx); return (EBUSY); } tp->tun_pid = td->td_proc->p_pid; tp->tun_flags |= TUN_OPEN; mtx_unlock(&tp->tun_mtx); ifp = &tp->tun_if; TUNDEBUG(ifp, "open\n"); return (0); } /* * tunclose - close the device - mark i/f down & delete * routing info */ static int tunclose(dev_t dev, int foo, int bar, struct thread *td) { struct tun_softc *tp; struct ifnet *ifp; int s; tp = dev->si_drv1; ifp = &tp->tun_if; mtx_lock(&tp->tun_mtx); tp->tun_flags &= ~TUN_OPEN; tp->tun_pid = 0; mtx_unlock(&tp->tun_mtx); /* * 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) { struct ifaddr *ifa; s = splimp(); /* find internet addresses and delete routes */ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (ifa->ifa_addr->sa_family == AF_INET) /* Unlocked read. */ rtinit(ifa, (int)RTM_DELETE, tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0); ifp->if_flags &= ~IFF_RUNNING; splx(s); } funsetown(&tp->tun_sigio); selwakeuppri(&tp->tun_rsel, PZERO + 1); TUNDEBUG (ifp, "closed\n"); return (0); } static int tuninit(struct ifnet *ifp) { struct tun_softc *tp = ifp->if_softc; struct ifaddr *ifa; int error = 0; TUNDEBUG(ifp, "tuninit\n"); ifp->if_flags |= IFF_UP | IFF_RUNNING; getmicrotime(&ifp->if_lastchange); 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; mtx_lock(&tp->tun_mtx); 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; mtx_unlock(&tp->tun_mtx); } #endif } } return (error); } /* * Process an ioctl request. */ static int tunifioctl(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; mtx_lock(&tp->tun_mtx); if (tp->tun_pid) sprintf(ifs->ascii + strlen(ifs->ascii), "\tOpened by PID %d\n", tp->tun_pid); mtx_unlock(&tp->tun_mtx); break; case SIOCSIFADDR: error = tuninit(ifp); TUNDEBUG(ifp, "address set, error=%d\n", error); break; case SIOCSIFDSTADDR: error = tuninit(ifp); TUNDEBUG(ifp, "destination address set, error=%d\n", error); break; case SIOCSIFMTU: ifp->if_mtu = ifr->ifr_mtu; TUNDEBUG(ifp, "mtu set\n"); break; case SIOCSIFFLAGS: case SIOCADDMULTI: case SIOCDELMULTI: break; default: error = EINVAL; } splx(s); return (error); } /* * tunoutput - queue packets from higher level ready to put out. */ static int tunoutput( struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt) { struct tun_softc *tp = ifp->if_softc; u_short cached_tun_flags; -#ifdef MAC int error; -#endif TUNDEBUG (ifp, "tunoutput\n"); #ifdef MAC error = mac_check_ifnet_transmit(ifp, m0); if (error) { m_freem(m0); return (error); } #endif /* Could be unlocked read? */ mtx_lock(&tp->tun_mtx); cached_tun_flags = tp->tun_flags; mtx_unlock(&tp->tun_mtx); if ((cached_tun_flags & TUN_READY) != TUN_READY) { TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); m_freem (m0); return (EHOSTDOWN); } if ((ifp->if_flags & IFF_UP) != IFF_UP) { 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) { uint32_t af = dst->sa_family; bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m0); } /* prepend sockaddr? this may abort if the mbuf allocation fails */ if (cached_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 (cached_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)) { + IFQ_HANDOFF(ifp, m0, error); + if (error) { ifp->if_collisions++; return (ENOBUFS); } ifp->if_opackets++; return (0); } /* * the cdevsw interface is now pretty minimal. */ static int tunioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) { 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(td)) != 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: mtx_lock(&tp->tun_mtx); if (*(int *)data) { tp->tun_flags |= TUN_LMODE; tp->tun_flags &= ~TUN_IFHEAD; } else tp->tun_flags &= ~TUN_LMODE; mtx_unlock(&tp->tun_mtx); break; case TUNSIFHEAD: mtx_lock(&tp->tun_mtx); if (*(int *)data) { tp->tun_flags |= TUN_IFHEAD; tp->tun_flags &= ~TUN_LMODE; } else tp->tun_flags &= ~TUN_IFHEAD; mtx_unlock(&tp->tun_mtx); break; case TUNGIFHEAD: /* Could be unlocked read? */ mtx_lock(&tp->tun_mtx); *(int *)data = (tp->tun_flags & TUN_IFHEAD) ? 1 : 0; mtx_unlock(&tp->tun_mtx); break; case TUNSIFMODE: /* deny this if UP */ if (tp->tun_if.if_flags & IFF_UP) return(EBUSY); switch (*(int *)data & ~IFF_MULTICAST) { case IFF_POINTOPOINT: case IFF_BROADCAST: tp->tun_if.if_flags &= ~(IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST); tp->tun_if.if_flags |= *(int *)data; break; default: return(EINVAL); } break; case TUNSIFPID: mtx_lock(&tp->tun_mtx); tp->tun_pid = curthread->td_proc->p_pid; mtx_unlock(&tp->tun_mtx); break; case FIONBIO: break; case FIOASYNC: mtx_lock(&tp->tun_mtx); if (*(int *)data) tp->tun_flags |= TUN_ASYNC; else tp->tun_flags &= ~TUN_ASYNC; mtx_unlock(&tp->tun_mtx); 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_t dev, struct uio *uio, int flag) { struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = &tp->tun_if; struct mbuf *m; int error=0, len, s; TUNDEBUG (ifp, "read\n"); mtx_lock(&tp->tun_mtx); if ((tp->tun_flags & TUN_READY) != TUN_READY) { mtx_unlock(&tp->tun_mtx); TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); return (EHOSTDOWN); } tp->tun_flags &= ~TUN_RWAIT; mtx_unlock(&tp->tun_mtx); s = splimp(); do { IF_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { if (flag & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } mtx_lock(&tp->tun_mtx); tp->tun_flags |= TUN_RWAIT; mtx_unlock(&tp->tun_mtx); if((error = tsleep(tp, PCATCH | (PZERO + 1), "tunread", 0)) != 0) { splx(s); return (error); } } } while (m == NULL); splx(s); while (m && uio->uio_resid > 0 && error == 0) { len = min(uio->uio_resid, m->m_len); if (len != 0) error = uiomove(mtod(m, void *), len, uio); m = m_free(m); } if (m) { TUNDEBUG(ifp, "Dropping mbuf\n"); m_freem(m); } return (error); } /* * the cdevsw write interface - an atomic write is a packet - or else! */ static int tunwrite(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; int isr; TUNDEBUG(ifp, "tunwrite\n"); if ((ifp->if_flags & IFF_UP) != IFF_UP) /* ignore silently */ return (0); if (uio->uio_resid == 0) return (0); if (uio->uio_resid < 0 || uio->uio_resid > TUNMRU) { TUNDEBUG(ifp, "len=%d!\n", 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, void *), 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; #ifdef MAC mac_create_mbuf_from_ifnet(ifp, top); #endif /* Could be unlocked read? */ mtx_lock(&tp->tun_mtx); if (tp->tun_flags & TUN_IFHEAD) { mtx_unlock(&tp->tun_mtx); 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 { mtx_unlock(&tp->tun_mtx); family = AF_INET; } BPF_MTAP2(ifp, &family, sizeof(family), top); switch (family) { #ifdef INET case AF_INET: isr = NETISR_IP; break; #endif #ifdef INET6 case AF_INET6: isr = NETISR_IPV6; break; #endif #ifdef IPX case AF_IPX: isr = NETISR_IPX; break; #endif #ifdef NETATALK case AF_APPLETALK: isr = NETISR_ATALK2; break; #endif default: m_freem(m); return (EAFNOSUPPORT); } /* First chunk of an mbuf contains good junk */ if (harvest.point_to_point) random_harvest(m, 16, 3, 0, RANDOM_NET); ifp->if_ibytes += top->m_pkthdr.len; ifp->if_ipackets++; netisr_dispatch(isr, top); return (0); } /* * 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_t dev, int events, struct thread *td) { int s; struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = &tp->tun_if; int revents = 0; s = splimp(); TUNDEBUG(ifp, "tunpoll\n"); if (events & (POLLIN | POLLRDNORM)) { if (ifp->if_snd.ifq_len > 0) { TUNDEBUG(ifp, "tunpoll q=%d\n", ifp->if_snd.ifq_len); revents |= events & (POLLIN | POLLRDNORM); } else { TUNDEBUG(ifp, "tunpoll waiting\n"); selrecord(td, &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 130548) +++ head/sys/net/if_vlan.c (revision 130549) @@ -1,849 +1,851 @@ /* * 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. */ #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #define VLANNAME "vlan" struct vlan_mc_entry { struct ether_addr mc_addr; SLIST_ENTRY(vlan_mc_entry) mc_entries; }; struct ifvlan { struct arpcom ifv_ac; /* make this an interface */ struct ifnet *ifv_p; /* parent inteface of this vlan */ struct ifv_linkmib { int ifvm_parent; int ifvm_encaplen; /* encapsulation length */ int ifvm_mtufudge; /* MTU fudged by this much */ int ifvm_mintu; /* min transmission unit */ u_int16_t ifvm_proto; /* encapsulation ethertype */ u_int16_t ifvm_tag; /* tag to apply on packets leaving if */ } ifv_mib; SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead; LIST_ENTRY(ifvlan) ifv_list; int ifv_flags; }; #define ifv_if ifv_ac.ac_if #define ifv_tag ifv_mib.ifvm_tag #define ifv_encaplen ifv_mib.ifvm_encaplen #define ifv_mtufudge ifv_mib.ifvm_mtufudge #define ifv_mintu ifv_mib.ifvm_mintu #define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); static MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface"); static LIST_HEAD(, ifvlan) ifv_list; /* * Locking: one lock is used to guard both the ifv_list and modification * to vlan data structures. We are rather conservative here; probably * more than necessary. */ static struct mtx ifv_mtx; #define VLAN_LOCK_INIT() mtx_init(&ifv_mtx, VLANNAME, NULL, MTX_DEF) #define VLAN_LOCK_DESTROY() mtx_destroy(&ifv_mtx) #define VLAN_LOCK_ASSERT() mtx_assert(&ifv_mtx, MA_OWNED) #define VLAN_LOCK() mtx_lock(&ifv_mtx) #define VLAN_UNLOCK() mtx_unlock(&ifv_mtx) static int vlan_clone_create(struct if_clone *, int); static void vlan_clone_destroy(struct ifnet *); static void vlan_start(struct ifnet *ifp); static void vlan_ifinit(void *foo); static void vlan_input(struct ifnet *ifp, struct mbuf *m); 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); static void vlan_link_state(struct ifnet *ifp, int link); struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME, vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT); /* * 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; /* * If we don't have a parent, just remember the membership for * when we do. */ if (ifp_p == NULL) return(0); bzero((char *)&sdl, sizeof sdl); sdl.sdl_len = sizeof sdl; sdl.sdl_family = AF_LINK; sdl.sdl_index = ifp_p->if_index; sdl.sdl_type = IFT_ETHER; sdl.sdl_alen = ETHER_ADDR_LEN; /* First, remove any existing filter entries. */ 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_VLAN); } /* Now program new ones. */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK); 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); bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), LLADDR(&sdl), ETHER_ADDR_LEN); error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); if (error) return(error); } return(0); } /* * VLAN support can be loaded as a module. The only place in the * system that's intimately aware of this is ether_input. We hook * into this code through vlan_input_p which is defined there and * set here. Noone else in the system should be aware of this so * we use an explicit reference here. * * NB: Noone should ever need to check if vlan_input_p is null or * not. This is because interfaces have a count of the number * of active vlans (if_nvlans) and this should never be bumped * except by vlan_config--which is in this module so therefore * the module must be loaded and vlan_input_p must be non-NULL. */ extern void (*vlan_input_p)(struct ifnet *, struct mbuf *); /* For MII eyes only... */ extern void (*vlan_link_state_p)(struct ifnet *, int); static int vlan_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: LIST_INIT(&ifv_list); VLAN_LOCK_INIT(); vlan_input_p = vlan_input; vlan_link_state_p = vlan_link_state; if_clone_attach(&vlan_cloner); break; case MOD_UNLOAD: if_clone_detach(&vlan_cloner); vlan_input_p = NULL; vlan_link_state_p = NULL; while (!LIST_EMPTY(&ifv_list)) vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if); VLAN_LOCK_DESTROY(); break; } return 0; } static moduledata_t vlan_mod = { "if_vlan", vlan_modevent, 0 }; DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_DEPEND(if_vlan, miibus, 1, 1, 1); static int vlan_clone_create(struct if_clone *ifc, int unit) { struct ifvlan *ifv; struct ifnet *ifp; ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); ifp = &ifv->ifv_if; SLIST_INIT(&ifv->vlan_mc_listhead); ifp->if_softc = ifv; if_initname(ifp, ifc->ifc_name, unit); /* NB: flags are not set here */ ifp->if_linkmib = &ifv->ifv_mib; ifp->if_linkmiblen = sizeof ifv->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_snd.ifq_maxlen = ifqmaxlen; ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr); /* Now undo some of the damage... */ ifp->if_baudrate = 0; ifp->if_type = IFT_L2VLAN; ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; VLAN_LOCK(); LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); VLAN_UNLOCK(); return (0); } static void vlan_clone_destroy(struct ifnet *ifp) { struct ifvlan *ifv = ifp->if_softc; VLAN_LOCK(); LIST_REMOVE(ifv, ifv_list); vlan_unconfig(ifp); VLAN_UNLOCK(); ether_ifdetach(ifp); free(ifv, M_VLAN); } 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; + int error; ifv = ifp->if_softc; p = ifv->ifv_p; ifp->if_flags |= IFF_OACTIVE; for (;;) { IF_DEQUEUE(&ifp->if_snd, m); if (m == 0) break; BPF_MTAP(ifp, m); /* * Do not run parent's if_start() if the parent is not up, * or parent's driver will cause a system crash. */ if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) { m_freem(m); ifp->if_collisions++; continue; } /* * If underlying interface can do VLAN tag insertion itself, * 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 attach a * packet tag that holds it. */ if (p->if_capenable & IFCAP_VLAN_HWTAGGING) { struct m_tag *mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof (u_int), M_NOWAIT); if (mtag == NULL) { ifp->if_oerrors++; m_freem(m); continue; } *(u_int*)(mtag+1) = ifv->ifv_tag; m_tag_prepend(m, mtag); } else { M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT); if (m == NULL) { if_printf(ifp, "unable to prepend VLAN header\n"); ifp->if_oerrors++; continue; } /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ if (m->m_len < sizeof(*evl)) { m = m_pullup(m, sizeof(*evl)); if (m == NULL) { if_printf(ifp, "cannot pullup VLAN header\n"); ifp->if_oerrors++; continue; } } /* * Transform the Ethernet header into an Ethernet header * with 802.1Q encapsulation. */ bcopy(mtod(m, char *) + ifv->ifv_encaplen, mtod(m, char *), ETHER_HDR_LEN); evl = mtod(m, struct ether_vlan_header *); evl->evl_proto = evl->evl_encap_proto; evl->evl_encap_proto = htons(ETHERTYPE_VLAN); evl->evl_tag = htons(ifv->ifv_tag); #ifdef DEBUG printf("vlan_start: %*D\n", (int)sizeof *evl, (unsigned 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)) + IFQ_HANDOFF(p, m, error); + if (!error) ifp->if_opackets++; else ifp->if_oerrors++; } ifp->if_flags &= ~IFF_OACTIVE; return; } static void vlan_input(struct ifnet *ifp, struct mbuf *m) { struct ether_vlan_header *evl; struct ifvlan *ifv; struct m_tag *mtag; u_int tag; mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL); if (mtag != NULL) { /* * Packet is tagged, m contains a normal * Ethernet frame; the tag is stored out-of-band. */ tag = EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)); m_tag_delete(m, mtag); } else { switch (ifp->if_type) { case IFT_ETHER: if (m->m_len < sizeof (*evl) && (m = m_pullup(m, sizeof (*evl))) == NULL) { if_printf(ifp, "cannot pullup VLAN header\n"); return; } evl = mtod(m, struct ether_vlan_header *); KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN, ("vlan_input: bad encapsulated protocols (%u)", ntohs(evl->evl_encap_proto))); tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); /* * Restore the original ethertype. We'll remove * the encapsulation after we've found the vlan * interface corresponding to the tag. */ evl->evl_encap_proto = evl->evl_proto; break; default: tag = (u_int) -1; #ifdef DIAGNOSTIC panic("vlan_input: unsupported if type %u", ifp->if_type); #endif break; } } VLAN_LOCK(); LIST_FOREACH(ifv, &ifv_list, ifv_list) if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) break; if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { VLAN_UNLOCK(); m_freem(m); ifp->if_noproto++; return; } VLAN_UNLOCK(); /* XXX extend below? */ if (mtag == NULL) { /* * Packet had an in-line encapsulation header; * remove it. The original header has already * been fixed up above. */ bcopy(mtod(m, caddr_t), mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN, ETHER_HDR_LEN); m_adj(m, ETHER_VLAN_ENCAP_LEN); } m->m_pkthdr.rcvif = &ifv->ifv_if; ifv->ifv_if.if_ipackets++; /* Pass it back through the parent's input routine. */ (*ifp->if_input)(&ifv->ifv_if, m); } static int vlan_config(struct ifvlan *ifv, struct ifnet *p) { struct ifaddr *ifa1, *ifa2; struct sockaddr_dl *sdl1, *sdl2; VLAN_LOCK_ASSERT(); if (p->if_data.ifi_type != IFT_ETHER) return EPROTONOSUPPORT; if (ifv->ifv_p) return EBUSY; ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; ifv->ifv_mintu = ETHERMIN; ifv->ifv_flags = 0; /* * If the parent supports the VLAN_MTU capability, * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, * use it. * First of all, enable Tx/Rx of such extended frames on the * parent if it's disabled and we're the first to attach. */ p->if_nvlans++; if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) && (p->if_capenable & IFCAP_VLAN_MTU) == 0) { struct ifreq ifr; int error; ifr.ifr_reqcap = p->if_capenable | IFCAP_VLAN_MTU; error = (*p->if_ioctl)(p, SIOCSIFCAP, (caddr_t) &ifr); if (error) { p->if_nvlans--; return (error); } } if (p->if_capenable & IFCAP_VLAN_MTU) { /* * No need to fudge the MTU since the parent can * handle extended frames. */ ifv->ifv_mtufudge = 0; } else { /* * Fudge the MTU by the encapsulation size. This * makes us incompatible with strictly compliant * 802.1Q implementations, but allows us to use * the feature with other NetBSD implementations, * which might still be useful. */ ifv->ifv_mtufudge = ifv->ifv_encaplen; } ifv->ifv_p = p; ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge; /* * Copy only a selected subset of flags from the parent. * Other flags are none of our business. */ ifv->ifv_if.if_flags = (p->if_flags & (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); ifv->ifv_if.if_link_state = p->if_link_state; #if 0 /* * Not ready yet. We need notification from the parent * when hw checksumming flags in its if_capenable change. * Flags set in if_capabilities only are useless. */ /* * If the parent interface can do hardware-assisted * VLAN encapsulation, then propagate its hardware- * assisted checksumming flags. */ if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM; #endif /* * Set up our ``Ethernet address'' to reflect the underlying * physical interface's. */ ifa1 = ifaddr_byindex(ifv->ifv_if.if_index); ifa2 = ifaddr_byindex(p->if_index); 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); /* * Configure multicast addresses that may already be * joined on the vlan device. */ (void)vlan_setmulti(&ifv->ifv_if); 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; VLAN_LOCK_ASSERT(); ifv = ifp->if_softc; p = ifv->ifv_p; if (p) { struct sockaddr_dl sdl; /* * Since the interface is being unconfigured, we need to * empty the list of multicast groups that we may have joined * while we were alive from the parent's list. */ bzero((char *)&sdl, sizeof sdl); sdl.sdl_len = sizeof sdl; sdl.sdl_family = AF_LINK; sdl.sdl_index = p->if_index; sdl.sdl_type = IFT_ETHER; sdl.sdl_alen = ETHER_ADDR_LEN; while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { mc = SLIST_FIRST(&ifv->vlan_mc_listhead); bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); error = if_delmulti(p, (struct sockaddr *)&sdl); if (error) return(error); SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); free(mc, M_VLAN); } p->if_nvlans--; if (p->if_nvlans == 0) { struct ifreq ifr; /* * Try to disable Tx/Rx of VLAN-sized frames. * This may have no effect for some interfaces, * but only the parent driver knows that. */ ifr.ifr_reqcap = p->if_capenable & ~IFCAP_VLAN_MTU; (*p->if_ioctl)(p, SIOCSIFCAP, (caddr_t) &ifr); } } /* Disconnect from parent. */ ifv->ifv_p = NULL; ifv->ifv_if.if_mtu = ETHERMTU; /* XXX why not 0? */ ifv->ifv_flags = 0; ifv->ifv_if.if_link_state = LINK_STATE_UNKNOWN; /* Clear our MAC address. */ ifa = ifaddr_byindex(ifv->ifv_if.if_index); 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_set_promisc(struct ifnet *ifp) { struct ifvlan *ifv = ifp->if_softc; int error = 0; if ((ifp->if_flags & IFF_PROMISC) != 0) { if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { error = ifpromisc(ifv->ifv_p, 1); if (error == 0) ifv->ifv_flags |= IFVF_PROMISC; } } else { if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { error = ifpromisc(ifv->ifv_p, 0); if (error == 0) ifv->ifv_flags &= ~IFVF_PROMISC; } } return (error); } /* Inform all vlans that their parent has changed link state */ static void vlan_link_state(struct ifnet *ifp, int link) { struct ifvlan *ifv; VLAN_LOCK(); LIST_FOREACH(ifv, &ifv_list, ifv_list) { if (ifv->ifv_p == ifp) { ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state; rt_ifmsg(&(ifv->ifv_if)); KNOTE(&ifp->if_klist, link); } } VLAN_UNLOCK(); } 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_if, ifa); break; #endif default: break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) &ifr->ifr_data; bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCGIFMEDIA: VLAN_LOCK(); if (ifv->ifv_p != NULL) { error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCGIFMEDIA, data); VLAN_UNLOCK(); /* Limit the result to the parent's current config. */ if (error == 0) { struct ifmediareq *ifmr; ifmr = (struct ifmediareq *) data; if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { ifmr->ifm_count = 1; error = copyout(&ifmr->ifm_current, ifmr->ifm_ulist, sizeof(int)); } } } else { VLAN_UNLOCK(); error = EINVAL; } break; case SIOCSIFMEDIA: error = EINVAL; break; case SIOCSIFMTU: /* * Set the interface MTU. */ VLAN_LOCK(); if (ifv->ifv_p != NULL) { if (ifr->ifr_mtu > (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) || ifr->ifr_mtu < (ifv->ifv_mintu - ifv->ifv_mtufudge)) error = EINVAL; else ifp->if_mtu = ifr->ifr_mtu; } else error = EINVAL; VLAN_UNLOCK(); break; case SIOCSETVLAN: error = copyin(ifr->ifr_data, &vlr, sizeof vlr); if (error) break; if (vlr.vlr_parent[0] == '\0') { VLAN_LOCK(); vlan_unconfig(ifp); if (ifp->if_flags & IFF_UP) if_down(ifp); ifp->if_flags &= ~IFF_RUNNING; VLAN_UNLOCK(); break; } p = ifunit(vlr.vlr_parent); if (p == 0) { error = ENOENT; break; } /* * Don't let the caller set up a VLAN tag with * anything except VLID bits. */ if (vlr.vlr_tag & ~EVL_VLID_MASK) { error = EINVAL; break; } VLAN_LOCK(); error = vlan_config(ifv, p); if (error) { VLAN_UNLOCK(); break; } ifv->ifv_tag = vlr.vlr_tag; ifp->if_flags |= IFF_RUNNING; VLAN_UNLOCK(); /* Update promiscuous mode, if necessary. */ vlan_set_promisc(ifp); break; case SIOCGETVLAN: bzero(&vlr, sizeof vlr); VLAN_LOCK(); if (ifv->ifv_p) { strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname, sizeof(vlr.vlr_parent)); vlr.vlr_tag = ifv->ifv_tag; } VLAN_UNLOCK(); error = copyout(&vlr, ifr->ifr_data, sizeof vlr); break; case SIOCSIFFLAGS: /* * For promiscuous mode, we enable promiscuous mode on * the parent if we need promiscuous on the VLAN interface. */ if (ifv->ifv_p != NULL) error = vlan_set_promisc(ifp); break; case SIOCADDMULTI: case SIOCDELMULTI: error = vlan_setmulti(ifp); break; default: error = EINVAL; } return error; }