if_iso88025subr.c revision 69152
1/* 2 * Copyright (c) 1998, Larry Lile 3 * All rights reserved. 4 * 5 * For latest sources and information on this driver, please 6 * go to http://anarchy.stdio.com. 7 * 8 * Questions, comments or suggestions should be directed to 9 * Larry Lile <lile@stdio.com>. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice unmodified, this list of conditions, and the following 16 * disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $FreeBSD: head/sys/net/if_iso88025subr.c 69152 2000-11-25 07:35:38Z jlemon $ 34 * 35 */ 36 37/* 38 * 39 * General ISO 802.5 (Token Ring) support routines 40 * 41 */ 42 43#include "opt_inet.h" 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/mbuf.h> 48#include <sys/socket.h> 49#include <sys/sockio.h> 50#include <sys/sysctl.h> 51 52#include <net/if.h> 53#include <net/netisr.h> 54#include <net/route.h> 55#include <net/if_llc.h> 56#include <net/if_dl.h> 57#include <net/if_types.h> 58 59#include <net/if_arp.h> 60 61#include <net/iso88025.h> 62 63#ifdef INET 64#include <netinet/in.h> 65#include <netinet/in_var.h> 66#include <netinet/if_ether.h> 67#endif 68 69#include <net/bpf.h> 70 71#include <machine/md_var.h> 72 73#include <i386/isa/isa_device.h> 74 75#include <vm/vm.h> 76#include <vm/vm_param.h> 77#include <vm/pmap.h> 78 79#include <net/iso88025.h> 80 81void 82iso88025_ifattach(struct ifnet *ifp) 83{ 84 register struct ifaddr *ifa = NULL; 85 register struct sockaddr_dl *sdl; 86 87 ifp->if_type = IFT_ISO88025; 88 ifp->if_addrlen = ISO88025_ADDR_LEN; 89 ifp->if_hdrlen = ISO88025_HDR_LEN; 90 if (ifp->if_baudrate == 0) 91 ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */ 92 if (ifp->if_mtu == 0) 93 ifp->if_mtu = ISO88025_DEFAULT_MTU; 94 95 ifa = ifnet_addrs[ifp->if_index - 1]; 96 if (ifa == 0) { 97 printf("iso88025_ifattach: no lladdr!\n"); 98 return; 99 } 100 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 101 sdl->sdl_type = IFT_ISO88025; 102 sdl->sdl_alen = ifp->if_addrlen; 103 bcopy(((struct arpcom *)ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); 104} 105 106int 107iso88025_ioctl(struct ifnet *ifp, int command, caddr_t data) 108{ 109 struct ifaddr *ifa = (struct ifaddr *) data; 110 struct ifreq *ifr = (struct ifreq *) data; 111 int error = 0; 112 113 switch (command) { 114 case SIOCSIFADDR: 115 ifp->if_flags |= IFF_UP; 116 117 switch (ifa->ifa_addr->sa_family) { 118#ifdef INET 119 case AF_INET: 120 ifp->if_init(ifp->if_softc); /* before arpwhohas */ 121 arp_ifinit((struct arpcom *)ifp, ifa); 122 break; 123#endif 124 default: 125 ifp->if_init(ifp->if_softc); 126 break; 127 } 128 break; 129 130 case SIOCGIFADDR: 131 { 132 struct sockaddr *sa; 133 134 sa = (struct sockaddr *) & ifr->ifr_data; 135 bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 136 (caddr_t) sa->sa_data, ISO88025_ADDR_LEN); 137 } 138 break; 139 140 case SIOCSIFMTU: 141 /* 142 * Set the interface MTU. 143 */ 144 if (ifr->ifr_mtu > ISO88025_MAX_MTU) { 145 error = EINVAL; 146 } else { 147 ifp->if_mtu = ifr->ifr_mtu; 148 } 149 break; 150 } 151 return (error); 152} 153 154/* 155 * ISO88025 encapsulation 156 */ 157int 158iso88025_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) 159{ 160 register struct iso88025_header *th; 161 struct iso88025_header gen_th; 162 register struct iso88025_sockaddr_data *sd = (struct iso88025_sockaddr_data *)dst->sa_data; 163 register struct llc *l; 164 register struct sockaddr_dl *sdl = NULL; 165 int error = 0, rif_len = 0; 166 u_char edst[6]; 167 register struct rtentry *rt; 168 int loop_copy = 0; 169 struct arpcom *ac = (struct arpcom *)ifp; 170 171 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 172 senderr(ENETDOWN); 173 rt = rt0; 174 if (rt) { 175 if ((rt->rt_flags & RTF_UP) == 0) { 176 rt0 = rt = rtalloc1(dst, 1, 0UL); 177 if (rt0) 178 rt->rt_refcnt--; 179 else 180 senderr(EHOSTUNREACH); 181 } 182 if (rt->rt_flags & RTF_GATEWAY) { 183 if (rt->rt_gwroute == 0) 184 goto lookup; 185 if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { 186 rtfree(rt); rt = rt0; 187 lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 188 0UL); 189 if ((rt = rt->rt_gwroute) == 0) 190 senderr(EHOSTUNREACH); 191 } 192 } 193 if (rt->rt_flags & RTF_REJECT) 194 if (rt->rt_rmx.rmx_expire == 0 || 195 time_second < rt->rt_rmx.rmx_expire) 196 senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); 197 } 198 199 /* Calculate routing info length based on arp table entry */ 200 if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway)) 201 if (sdl->sdl_rcf != NULL) 202 rif_len = TR_RCF_RIFLEN(sdl->sdl_rcf); 203 204 /* Generate a generic 802.5 header for the packet */ 205 gen_th.ac = TR_AC; 206 gen_th.fc = TR_LLC_FRAME; 207 memcpy(gen_th.iso88025_shost, ac->ac_enaddr, sizeof(ac->ac_enaddr)); 208 if (rif_len) { 209 gen_th.iso88025_shost[0] |= TR_RII; 210 if (rif_len > 2) { 211 gen_th.rcf = sdl->sdl_rcf; 212 memcpy(gen_th.rd, sdl->sdl_route, rif_len - 2); 213 } 214 } 215 216 217 switch (dst->sa_family) { 218#ifdef INET 219 case AF_INET: 220 if (!arpresolve(ac, rt, m, dst, edst, rt0)) 221 return (0); /* if not yet resolved */ 222 /* Add LLC and SNAP headers */ 223 M_PREPEND(m, 8, M_DONTWAIT); 224 if (m == 0) 225 senderr(ENOBUFS); 226 l = mtod(m, struct llc *); 227 l->llc_un.type_snap.ether_type = htons(ETHERTYPE_IP); 228 l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP; 229 l->llc_un.type_snap.control = LLC_UI; 230 l->llc_un.type_snap.org_code[0] = 0x0; 231 l->llc_un.type_snap.org_code[1] = 0x0; 232 l->llc_un.type_snap.org_code[2] = 0x0; 233 memcpy(gen_th.iso88025_dhost, edst, sizeof(edst)); 234 break; 235#endif 236 237 case AF_UNSPEC: 238 /* 239 * For AF_UNSPEC sockaddr.sa_data must contain all of the 240 * mac information needed to send the packet. This allows 241 * full mac, llc, and source routing function to be controlled. 242 * llc and source routing information must already be in the 243 * mbuf provided, ac/fc are set in sa_data. sockaddr.sa_data 244 * should be a iso88025_sockaddr_data structure see iso88025.h 245 */ 246 loop_copy = -1; 247 sd = (struct iso88025_sockaddr_data *)dst->sa_data; 248 gen_th.ac = sd->ac; 249 gen_th.fc = sd->fc; 250 memcpy(gen_th.iso88025_dhost, sd->ether_dhost, sizeof(sd->ether_dhost)); 251 memcpy(gen_th.iso88025_shost, sd->ether_shost, sizeof(sd->ether_shost)); 252 rif_len = 0; 253 break; 254 255 default: 256 printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, 257 dst->sa_family); 258 senderr(EAFNOSUPPORT); 259 } 260 261 /* 262 * Add local net header. If no space in first mbuf, 263 * allocate another. 264 */ 265 266 M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT); 267 if (m == 0) 268 senderr(ENOBUFS); 269 270 /* Copy as much of the generic header as is needed into the mbuf */ 271 th = mtod(m, struct iso88025_header *); 272 memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len); 273 274 /* 275 * If a simplex interface, and the packet is being sent to our 276 * Ethernet address or a broadcast address, loopback a copy. 277 * XXX To make a simplex device behave exactly like a duplex 278 * device, we should copy in the case of sending to our own 279 * ethernet address (thus letting the original actually appear 280 * on the wire). However, we don't do that here for security 281 * reasons and compatibility with the original behavior. 282 */ 283 if ((ifp->if_flags & IFF_SIMPLEX) && 284 (loop_copy != -1)) { 285 if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { 286 struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); 287 (void) if_simloop(ifp, 288 n, dst->sa_family, ISO88025_HDR_LEN); 289 } else if (bcmp(th->iso88025_dhost, 290 th->iso88025_shost, ETHER_ADDR_LEN) == 0) { 291 (void) if_simloop(ifp, 292 m, dst->sa_family, ISO88025_HDR_LEN); 293 return(0); /* XXX */ 294 } 295 } 296 297 if (! IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, ISO88025_HDR_LEN + 8)) { 298 printf("iso88025_output: packet dropped QFULL.\n"); 299 senderr(ENOBUFS); 300 } 301 return (error); 302 303bad: 304 if (m) 305 m_freem(m); 306 return (error); 307} 308 309/* 310 * ISO 88025 de-encapsulation 311 */ 312void 313iso88025_input(struct ifnet *ifp, struct iso88025_header *th, struct mbuf *m) 314{ 315 register struct ifqueue *inq; 316 u_short ether_type; 317 register struct llc *l = mtod(m, struct llc *); 318 319 if ((ifp->if_flags & IFF_UP) == 0) { 320 m_freem(m); 321 return; 322 } 323 324 switch (l->llc_control) { 325 case LLC_UI: 326 break; 327 case LLC_TEST: 328 case LLC_TEST_P: 329 { 330 struct sockaddr sa; 331 struct arpcom *ac = (struct arpcom *)ifp; 332 struct iso88025_sockaddr_data *th2; 333 int i; 334 u_char c = l->llc_dsap; 335 336 if (th->iso88025_shost[0] & TR_RII) { /* XXX */ 337 printf("iso88025_input: dropping source routed LLC_TEST\n"); 338 m_free(m); 339 return; 340 } 341 l->llc_dsap = l->llc_ssap; 342 l->llc_ssap = c; 343 if (m->m_flags & (M_BCAST | M_MCAST)) 344 bcopy((caddr_t)ac->ac_enaddr, 345 (caddr_t)th->iso88025_dhost, ISO88025_ADDR_LEN); 346 sa.sa_family = AF_UNSPEC; 347 sa.sa_len = sizeof(sa); 348 th2 = (struct iso88025_sockaddr_data *)sa.sa_data; 349 for (i = 0; i < ISO88025_ADDR_LEN; i++) { 350 th2->ether_shost[i] = c = th->iso88025_dhost[i]; 351 th2->ether_dhost[i] = th->iso88025_dhost[i] = th->iso88025_shost[i]; 352 th->iso88025_shost[i] = c; 353 } 354 th2->ac = TR_AC; 355 th2->fc = TR_LLC_FRAME; 356 ifp->if_output(ifp, m, &sa, NULL); 357 return; 358 } 359 default: 360 printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control); 361 m_freem(m); 362 return; 363 } 364 365 m->m_pkthdr.len -= 8; 366 m->m_len -= 8; 367 m->m_data += 8; /* Length of LLC header in our case */ 368 369 ifp->if_ibytes += m->m_pkthdr.len + sizeof(*th); 370 if (th->iso88025_dhost[0] & 1) { 371 if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)th->iso88025_dhost, sizeof(etherbroadcastaddr)) == 0) 372 m->m_flags |= M_BCAST; 373 else 374 m->m_flags |= M_MCAST; 375 } 376 if (m->m_flags & (M_BCAST|M_MCAST)) 377 ifp->if_imcasts++; 378 379 ether_type = ntohs(l->llc_un.type_snap.ether_type); 380 381 switch (ether_type) { 382#ifdef INET 383 case ETHERTYPE_IP: 384 th->iso88025_shost[0] &= ~(TR_RII); 385 if (ipflow_fastforward(m)) 386 return; 387 schednetisr(NETISR_IP); 388 inq = &ipintrq; 389 break; 390 391 case ETHERTYPE_ARP: 392 schednetisr(NETISR_ARP); 393 inq = &arpintrq; 394 break; 395#endif 396 default: 397 m_freem(m); 398 return; 399 } 400 401 if (! IF_HANDOFF(inq, m, NULL)) 402 printf("iso88025_input: Packet dropped (Queue full).\n"); 403} 404