xref: /NextBSD/sys/net/if_iso88025subr.c (revision e28a391bb0759c6a63157d78495c8b1ee9ad3c08)
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$
34  *
35  */
36 
37 /*
38  *
39  * General ISO 802.5 (Token Ring) support routines
40  *
41  */
42 
43 #include "opt_inet.h"
44 #include "opt_inet6.h"
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #include <sys/mbuf.h>
51 #include <sys/module.h>
52 #include <sys/socket.h>
53 #include <sys/sockio.h>
54 
55 #include <net/if.h>
56 #include <net/if_var.h>
57 #include <net/if_arp.h>
58 #include <net/if_dl.h>
59 #include <net/if_llc.h>
60 #include <net/if_types.h>
61 #include <net/if_llatbl.h>
62 
63 #include <net/ethernet.h>
64 #include <net/netisr.h>
65 #include <net/route.h>
66 #include <net/bpf.h>
67 #include <net/iso88025.h>
68 
69 #if defined(INET) || defined(INET6)
70 #include <netinet/in.h>
71 #include <netinet/in_var.h>
72 #include <netinet/if_ether.h>
73 #endif
74 #ifdef INET6
75 #include <netinet6/nd6.h>
76 #endif
77 
78 #include <security/mac/mac_framework.h>
79 
80 static const u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] =
81 			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
82 
83 static int iso88025_resolvemulti (struct ifnet *, struct sockaddr **,
84 				  struct sockaddr *);
85 
86 #define	senderr(e)	do { error = (e); goto bad; } while (0)
87 
88 /*
89  * Perform common duties while attaching to interface list
90  */
91 void
iso88025_ifattach(struct ifnet * ifp,const u_int8_t * lla,int bpf)92 iso88025_ifattach(struct ifnet *ifp, const u_int8_t *lla, int bpf)
93 {
94     struct ifaddr *ifa;
95     struct sockaddr_dl *sdl;
96 
97     ifa = NULL;
98 
99     ifp->if_type = IFT_ISO88025;
100     ifp->if_addrlen = ISO88025_ADDR_LEN;
101     ifp->if_hdrlen = ISO88025_HDR_LEN;
102 
103     if_attach(ifp);	/* Must be called before additional assignments */
104 
105     ifp->if_output = iso88025_output;
106     ifp->if_input = iso88025_input;
107     ifp->if_resolvemulti = iso88025_resolvemulti;
108     ifp->if_broadcastaddr = iso88025_broadcastaddr;
109 
110     if (ifp->if_baudrate == 0)
111         ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */
112     if (ifp->if_mtu == 0)
113         ifp->if_mtu = ISO88025_DEFAULT_MTU;
114 
115     ifa = ifp->if_addr;
116     KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
117 
118     sdl = (struct sockaddr_dl *)ifa->ifa_addr;
119     sdl->sdl_type = IFT_ISO88025;
120     sdl->sdl_alen = ifp->if_addrlen;
121     bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
122 
123     if (bpf)
124         bpfattach(ifp, DLT_IEEE802, ISO88025_HDR_LEN);
125 
126     return;
127 }
128 
129 /*
130  * Perform common duties while detaching a Token Ring interface
131  */
132 void
iso88025_ifdetach(ifp,bpf)133 iso88025_ifdetach(ifp, bpf)
134         struct ifnet *ifp;
135         int bpf;
136 {
137 
138 	if (bpf)
139                 bpfdetach(ifp);
140 
141 	if_detach(ifp);
142 
143 	return;
144 }
145 
146 int
iso88025_ioctl(struct ifnet * ifp,u_long command,caddr_t data)147 iso88025_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
148 {
149         struct ifaddr *ifa;
150         struct ifreq *ifr;
151         int error;
152 
153 	ifa = (struct ifaddr *) data;
154 	ifr = (struct ifreq *) data;
155 	error = 0;
156 
157         switch (command) {
158         case SIOCSIFADDR:
159                 ifp->if_flags |= IFF_UP;
160 
161                 switch (ifa->ifa_addr->sa_family) {
162 #ifdef INET
163                 case AF_INET:
164                         ifp->if_init(ifp->if_softc);    /* before arpwhohas */
165                         arp_ifinit(ifp, ifa);
166                         break;
167 #endif	/* INET */
168                 default:
169                         ifp->if_init(ifp->if_softc);
170                         break;
171                 }
172                 break;
173 
174         case SIOCGIFADDR: {
175                         struct sockaddr *sa;
176 
177                         sa = (struct sockaddr *) & ifr->ifr_data;
178                         bcopy(IF_LLADDR(ifp),
179                               (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
180                 }
181                 break;
182 
183         case SIOCSIFMTU:
184                 /*
185                  * Set the interface MTU.
186                  */
187                 if (ifr->ifr_mtu > ISO88025_MAX_MTU) {
188                         error = EINVAL;
189                 } else {
190                         ifp->if_mtu = ifr->ifr_mtu;
191                 }
192                 break;
193 	default:
194 		error = EINVAL;			/* XXX netbsd has ENOTTY??? */
195 		break;
196         }
197 
198         return (error);
199 }
200 
201 /*
202  * ISO88025 encapsulation
203  */
204 int
iso88025_output(struct ifnet * ifp,struct mbuf * m,const struct sockaddr * dst,struct route * ro)205 iso88025_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
206 	struct route *ro)
207 {
208 	u_int16_t snap_type = 0;
209 	int loop_copy = 0, error = 0, rif_len = 0;
210 	u_char edst[ISO88025_ADDR_LEN];
211 	struct iso88025_header *th;
212 	struct iso88025_header gen_th;
213 	struct sockaddr_dl *sdl = NULL;
214 	struct rtentry *rt0 = NULL;
215 	int is_gw = 0;
216 
217 	if (ro != NULL)
218 		is_gw = (ro->ro_flags & RT_HAS_GW) != 0;
219 #ifdef MAC
220 	error = mac_ifnet_check_transmit(ifp, m);
221 	if (error)
222 		senderr(error);
223 #endif
224 
225 	if (ifp->if_flags & IFF_MONITOR)
226 		senderr(ENETDOWN);
227 	if (!((ifp->if_flags & IFF_UP) &&
228 	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
229 		senderr(ENETDOWN);
230 	getmicrotime(&ifp->if_lastchange);
231 
232 	/* Calculate routing info length based on arp table entry */
233 	/* XXX any better way to do this ? */
234 
235 	if (rt0 && (sdl = (struct sockaddr_dl *)rt0->rt_gateway))
236 		if (SDL_ISO88025(sdl)->trld_rcf != 0)
237 			rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf);
238 
239 	/* Generate a generic 802.5 header for the packet */
240 	gen_th.ac = TR_AC;
241 	gen_th.fc = TR_LLC_FRAME;
242 	(void)memcpy((caddr_t)gen_th.iso88025_shost, IF_LLADDR(ifp),
243 		     ISO88025_ADDR_LEN);
244 	if (rif_len) {
245 		gen_th.iso88025_shost[0] |= TR_RII;
246 		if (rif_len > 2) {
247 			gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf;
248 			(void)memcpy((caddr_t)gen_th.rd,
249 				(caddr_t)SDL_ISO88025(sdl)->trld_route,
250 				rif_len - 2);
251 		}
252 	}
253 
254 	switch (dst->sa_family) {
255 #ifdef INET
256 	case AF_INET:
257 		error = arpresolve(ifp, is_gw, m, dst, edst, NULL);
258 		if (error)
259 			return (error == EWOULDBLOCK ? 0 : error);
260 		snap_type = ETHERTYPE_IP;
261 		break;
262 	case AF_ARP:
263 	{
264 		struct arphdr *ah;
265 		ah = mtod(m, struct arphdr *);
266 		ah->ar_hrd = htons(ARPHRD_IEEE802);
267 
268 		loop_copy = -1; /* if this is for us, don't do it */
269 
270 		switch(ntohs(ah->ar_op)) {
271 		case ARPOP_REVREQUEST:
272 		case ARPOP_REVREPLY:
273 			snap_type = ETHERTYPE_REVARP;
274 			break;
275 		case ARPOP_REQUEST:
276 		case ARPOP_REPLY:
277 		default:
278 			snap_type = ETHERTYPE_ARP;
279 			break;
280 		}
281 
282 		if (m->m_flags & M_BCAST)
283 			bcopy(ifp->if_broadcastaddr, edst, ISO88025_ADDR_LEN);
284 		else
285 			bcopy(ar_tha(ah), edst, ISO88025_ADDR_LEN);
286 
287 	}
288 	break;
289 #endif	/* INET */
290 #ifdef INET6
291 	case AF_INET6:
292 		error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL);
293 		if (error)
294 			return (error == EWOULDBLOCK ? 0 : error);
295 		snap_type = ETHERTYPE_IPV6;
296 		break;
297 #endif	/* INET6 */
298 	case AF_UNSPEC:
299 	{
300 		const struct iso88025_sockaddr_data *sd;
301 		/*
302 		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
303 		 * mac information needed to send the packet.  This allows
304 		 * full mac, llc, and source routing function to be controlled.
305 		 * llc and source routing information must already be in the
306 		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
307 		 * should be an iso88025_sockaddr_data structure see iso88025.h
308 		 */
309                 loop_copy = -1;
310 		sd = (const struct iso88025_sockaddr_data *)dst->sa_data;
311 		gen_th.ac = sd->ac;
312 		gen_th.fc = sd->fc;
313 		(void)memcpy(edst, sd->ether_dhost, ISO88025_ADDR_LEN);
314 		(void)memcpy(gen_th.iso88025_shost, sd->ether_shost,
315 		    ISO88025_ADDR_LEN);
316 		rif_len = 0;
317 		break;
318 	}
319 	default:
320 		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
321 		senderr(EAFNOSUPPORT);
322 		break;
323 	}
324 
325 	/*
326 	 * Add LLC header.
327 	 */
328 	if (snap_type != 0) {
329         	struct llc *l;
330 		M_PREPEND(m, LLC_SNAPFRAMELEN, M_NOWAIT);
331 		if (m == 0)
332 			senderr(ENOBUFS);
333 		l = mtod(m, struct llc *);
334 		l->llc_control = LLC_UI;
335 		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
336 		l->llc_snap.org_code[0] =
337 			l->llc_snap.org_code[1] =
338 			l->llc_snap.org_code[2] = 0;
339 		l->llc_snap.ether_type = htons(snap_type);
340 	}
341 
342 	/*
343 	 * Add local net header.  If no space in first mbuf,
344 	 * allocate another.
345 	 */
346 	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_NOWAIT);
347 	if (m == 0)
348 		senderr(ENOBUFS);
349 	th = mtod(m, struct iso88025_header *);
350 	bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN);
351 
352 	/* Copy as much of the generic header as is needed into the mbuf */
353 	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
354 
355         /*
356          * If a simplex interface, and the packet is being sent to our
357          * Ethernet address or a broadcast address, loopback a copy.
358          * XXX To make a simplex device behave exactly like a duplex
359          * device, we should copy in the case of sending to our own
360          * ethernet address (thus letting the original actually appear
361          * on the wire). However, we don't do that here for security
362          * reasons and compatibility with the original behavior.
363          */
364         if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
365                 if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
366                         struct mbuf *n;
367 			n = m_copy(m, 0, (int)M_COPYALL);
368                         (void) if_simloop(ifp, n, dst->sa_family,
369 					  ISO88025_HDR_LEN);
370                 } else if (bcmp(th->iso88025_dhost, th->iso88025_shost,
371 				 ETHER_ADDR_LEN) == 0) {
372 			(void) if_simloop(ifp, m, dst->sa_family,
373 					  ISO88025_HDR_LEN);
374                        	return(0);      /* XXX */
375 		}
376         }
377 
378 	IFQ_HANDOFF_ADJ(ifp, m, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN, error);
379 	if (error) {
380 		printf("iso88025_output: packet dropped QFULL.\n");
381 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
382 	}
383 	return (error);
384 
385 bad:
386 	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
387 	if (m)
388 		m_freem(m);
389 	return (error);
390 }
391 
392 /*
393  * ISO 88025 de-encapsulation
394  */
395 void
iso88025_input(ifp,m)396 iso88025_input(ifp, m)
397 	struct ifnet *ifp;
398 	struct mbuf *m;
399 {
400 	struct iso88025_header *th;
401 	struct llc *l;
402 	int isr;
403 	int mac_hdr_len;
404 
405 	/*
406 	 * Do consistency checks to verify assumptions
407 	 * made by code past this point.
408 	 */
409 	if ((m->m_flags & M_PKTHDR) == 0) {
410 		if_printf(ifp, "discard frame w/o packet header\n");
411 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
412 		m_freem(m);
413 		return;
414 	}
415 	if (m->m_pkthdr.rcvif == NULL) {
416 		if_printf(ifp, "discard frame w/o interface pointer\n");
417 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
418  		m_freem(m);
419 		return;
420 	}
421 
422 	m = m_pullup(m, ISO88025_HDR_LEN);
423 	if (m == NULL) {
424 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
425 		goto dropanyway;
426 	}
427 	th = mtod(m, struct iso88025_header *);
428 
429 	/*
430 	 * Discard packet if interface is not up.
431 	 */
432 	if (!((ifp->if_flags & IFF_UP) &&
433 	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
434 		goto dropanyway;
435 
436 	/*
437 	 * Give bpf a chance at the packet.
438 	 */
439 	BPF_MTAP(ifp, m);
440 
441 	/*
442 	 * Interface marked for monitoring; discard packet.
443 	 */
444 	if (ifp->if_flags & IFF_MONITOR) {
445 		m_freem(m);
446 		return;
447 	}
448 
449 #ifdef MAC
450 	mac_ifnet_create_mbuf(ifp, m);
451 #endif
452 
453 	/*
454 	 * Update interface statistics.
455 	 */
456 	if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
457 	getmicrotime(&ifp->if_lastchange);
458 
459 	/*
460 	 * Discard non local unicast packets when interface
461 	 * is in promiscuous mode.
462 	 */
463 	if ((ifp->if_flags & IFF_PROMISC) &&
464 	    ((th->iso88025_dhost[0] & 1) == 0) &&
465 	     (bcmp(IF_LLADDR(ifp), (caddr_t) th->iso88025_dhost,
466 	     ISO88025_ADDR_LEN) != 0))
467 		goto dropanyway;
468 
469 	/*
470 	 * Set mbuf flags for bcast/mcast.
471 	 */
472 	if (th->iso88025_dhost[0] & 1) {
473 		if (bcmp(iso88025_broadcastaddr, th->iso88025_dhost,
474 		    ISO88025_ADDR_LEN) == 0)
475 			m->m_flags |= M_BCAST;
476 		else
477 			m->m_flags |= M_MCAST;
478 		if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1);
479 	}
480 
481 	mac_hdr_len = ISO88025_HDR_LEN;
482 	/* Check for source routing info */
483 	if (th->iso88025_shost[0] & TR_RII)
484 		mac_hdr_len += TR_RCF_RIFLEN(th->rcf);
485 
486 	/* Strip off ISO88025 header. */
487 	m_adj(m, mac_hdr_len);
488 
489 	m = m_pullup(m, LLC_SNAPFRAMELEN);
490 	if (m == 0) {
491 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
492 		goto dropanyway;
493 	}
494 	l = mtod(m, struct llc *);
495 
496 	switch (l->llc_dsap) {
497 	case LLC_SNAP_LSAP: {
498 		u_int16_t type;
499 		if ((l->llc_control != LLC_UI) ||
500 		    (l->llc_ssap != LLC_SNAP_LSAP)) {
501 			if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
502 			goto dropanyway;
503 		}
504 
505 		if (l->llc_snap.org_code[0] != 0 ||
506 		    l->llc_snap.org_code[1] != 0 ||
507 		    l->llc_snap.org_code[2] != 0) {
508 			if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
509 			goto dropanyway;
510 		}
511 
512 		type = ntohs(l->llc_snap.ether_type);
513 		m_adj(m, LLC_SNAPFRAMELEN);
514 		switch (type) {
515 #ifdef INET
516 		case ETHERTYPE_IP:
517 			th->iso88025_shost[0] &= ~(TR_RII);
518 			isr = NETISR_IP;
519 			break;
520 
521 		case ETHERTYPE_ARP:
522 			if (ifp->if_flags & IFF_NOARP)
523 				goto dropanyway;
524 			isr = NETISR_ARP;
525 			break;
526 #endif	/* INET */
527 #ifdef INET6
528 		case ETHERTYPE_IPV6:
529 			th->iso88025_shost[0] &= ~(TR_RII);
530 			isr = NETISR_IPV6;
531 			break;
532 #endif	/* INET6 */
533 		default:
534 			printf("iso88025_input: unexpected llc_snap ether_type  0x%02x\n", type);
535 			if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
536 			goto dropanyway;
537 		}
538 		break;
539 	}
540 #ifdef ISO
541 	case LLC_ISO_LSAP:
542 		switch (l->llc_control) {
543 		case LLC_UI:
544 			if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
545 			goto dropanyway;
546 			break;
547                 case LLC_XID:
548                 case LLC_XID_P:
549 			if(m->m_len < ISO88025_ADDR_LEN)
550 				goto dropanyway;
551 			l->llc_window = 0;
552 			l->llc_fid = 9;
553 			l->llc_class = 1;
554 			l->llc_dsap = l->llc_ssap = 0;
555 			/* Fall through to */
556 		case LLC_TEST:
557 		case LLC_TEST_P:
558 		{
559 			struct sockaddr sa;
560 			struct iso88025_sockaddr_data *th2;
561 			int i;
562 			u_char c;
563 
564 			c = l->llc_dsap;
565 
566 			if (th->iso88025_shost[0] & TR_RII) { /* XXX */
567 				printf("iso88025_input: dropping source routed LLC_TEST\n");
568 				goto dropanyway;
569 			}
570 			l->llc_dsap = l->llc_ssap;
571 			l->llc_ssap = c;
572 			if (m->m_flags & (M_BCAST | M_MCAST))
573 				bcopy((caddr_t)IF_LLADDR(ifp),
574 				      (caddr_t)th->iso88025_dhost,
575 					ISO88025_ADDR_LEN);
576 			sa.sa_family = AF_UNSPEC;
577 			sa.sa_len = sizeof(sa);
578 			th2 = (struct iso88025_sockaddr_data *)sa.sa_data;
579 			for (i = 0; i < ISO88025_ADDR_LEN; i++) {
580 				th2->ether_shost[i] = c = th->iso88025_dhost[i];
581 				th2->ether_dhost[i] = th->iso88025_dhost[i] =
582 					th->iso88025_shost[i];
583 				th->iso88025_shost[i] = c;
584 			}
585 			th2->ac = TR_AC;
586 			th2->fc = TR_LLC_FRAME;
587 			ifp->if_output(ifp, m, &sa, NULL);
588 			return;
589 		}
590 		default:
591 			printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control);
592 			if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
593 			goto dropanyway;
594 			break;
595 		}
596 		break;
597 #endif	/* ISO */
598 	default:
599 		printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap);
600 		if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1);
601 		goto dropanyway;
602 		break;
603 	}
604 
605 	M_SETFIB(m, ifp->if_fib);
606 	netisr_dispatch(isr, m);
607 	return;
608 
609 dropanyway:
610 	if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
611 	if (m)
612 		m_freem(m);
613 	return;
614 }
615 
616 static int
iso88025_resolvemulti(ifp,llsa,sa)617 iso88025_resolvemulti (ifp, llsa, sa)
618 	struct ifnet *ifp;
619 	struct sockaddr **llsa;
620 	struct sockaddr *sa;
621 {
622 	struct sockaddr_dl *sdl;
623 #ifdef INET
624 	struct sockaddr_in *sin;
625 #endif
626 #ifdef INET6
627 	struct sockaddr_in6 *sin6;
628 #endif
629 	u_char *e_addr;
630 
631 	switch(sa->sa_family) {
632 	case AF_LINK:
633 		/*
634 		 * No mapping needed. Just check that it's a valid MC address.
635 		 */
636 		sdl = (struct sockaddr_dl *)sa;
637 		e_addr = LLADDR(sdl);
638 		if ((e_addr[0] & 1) != 1) {
639 			return (EADDRNOTAVAIL);
640 		}
641 		*llsa = 0;
642 		return (0);
643 
644 #ifdef INET
645 	case AF_INET:
646 		sin = (struct sockaddr_in *)sa;
647 		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
648 			return (EADDRNOTAVAIL);
649 		}
650 		sdl = link_init_sdl(ifp, *llsa, IFT_ISO88025);
651 		sdl->sdl_alen = ISO88025_ADDR_LEN;
652 		e_addr = LLADDR(sdl);
653 		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
654 		*llsa = (struct sockaddr *)sdl;
655 		return (0);
656 #endif
657 #ifdef INET6
658 	case AF_INET6:
659 		sin6 = (struct sockaddr_in6 *)sa;
660 		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
661 			/*
662 			 * An IP6 address of 0 means listen to all
663 			 * of the Ethernet multicast address used for IP6.
664 			 * (This is used for multicast routers.)
665 			 */
666 			ifp->if_flags |= IFF_ALLMULTI;
667 			*llsa = 0;
668 			return (0);
669 		}
670 		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
671 			return (EADDRNOTAVAIL);
672 		}
673 		sdl = link_init_sdl(ifp, *llsa, IFT_ISO88025);
674 		sdl->sdl_alen = ISO88025_ADDR_LEN;
675 		e_addr = LLADDR(sdl);
676 		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
677 		*llsa = (struct sockaddr *)sdl;
678 		return (0);
679 #endif
680 
681 	default:
682 		/*
683 		 * Well, the text isn't quite right, but it's the name
684 		 * that counts...
685 		 */
686 		return (EAFNOSUPPORT);
687 	}
688 
689 	return (0);
690 }
691 
692 static moduledata_t iso88025_mod = {
693 	.name = "iso88025",
694 };
695 
696 DECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
697 MODULE_VERSION(iso88025, 1);
698