2 * IS-IS Rout(e)ing protocol - isis_dlpi.c
4 * Copyright (C) 2001,2002 Sampo Saaristo
5 * Tampere University of Technology
6 * Institute of Communications Engineering
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public Licenseas published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
13 * This program is distributed in the hope that it will be useful,but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #if ISIS_METHOD == ISIS_METHOD_DLPI
26 #include <netinet/if_ether.h>
27 #include <sys/types.h>
33 #include <sys/pfmod.h>
40 #include "isisd/dict.h"
41 #include "isisd/include-netbsd/iso.h"
42 #include "isisd/isis_constants.h"
43 #include "isisd/isis_common.h"
44 #include "isisd/isis_circuit.h"
45 #include "isisd/isis_flags.h"
46 #include "isisd/isisd.h"
47 #include "isisd/isis_network.h"
51 extern struct zebra_privs_t isisd_privs;
53 static t_uscalar_t dlpi_ctl[1024]; /* DLPI control messages */
56 * Table 9 - Architectural constants for use with ISO 8802 subnetworks
60 u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
61 u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
62 u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
63 u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
65 static u_char sock_buff[8192];
67 static u_short pf_filter[] =
69 ENF_PUSHWORD + 0, /* Get the SSAP/DSAP values */
70 ENF_PUSHLIT | ENF_CAND, /* Check them */
71 ISO_SAP | (ISO_SAP << 8),
72 ENF_PUSHWORD + 1, /* Get the control value */
73 ENF_PUSHLIT | ENF_AND, /* Isolate it */
79 ENF_PUSHLIT | ENF_CAND, /* Test for expected value */
88 * We would like to use something like libdlpi here, but that's not present on
89 * all versions of Solaris or on any non-Solaris system, so it's nowhere near
90 * as portable as we'd like. Thus, we use the standards-conformant DLPI
91 * interfaces plus the (optional; not needed) Solaris packet filter module.
95 dlpisend (int fd, const void *cbuf, size_t cbuflen,
96 const void *dbuf, size_t dbuflen, int flags)
98 const struct strbuf *ctlptr = NULL;
99 const struct strbuf *dataptr = NULL;
100 struct strbuf ctlbuf, databuf;
105 memset (&ctlbuf, 0, sizeof (ctlbuf));
106 ctlbuf.len = cbuflen;
107 ctlbuf.buf = (void *)cbuf;
113 memset (&databuf, 0, sizeof (databuf));
114 databuf.len = dbuflen;
115 databuf.buf = (void *)dbuf;
119 /* We assume this doesn't happen often and isn't operationally significant */
120 rv = putmsg(fd, ctlptr, dataptr, flags);
121 if (rv == -1 && dbuf == NULL)
124 * For actual PDU transmission - recognizable buf dbuf != NULL,
125 * the error is passed upwards and should not be printed here.
127 zlog_debug ("%s: putmsg: %s", __func__, safe_strerror (errno));
135 struct pollfd fds[1];
136 struct strbuf ctlbuf, databuf;
141 /* Poll is used here in case the device doesn't speak DLPI correctly */
142 memset (fds, 0, sizeof (fds));
144 fds[0].events = POLLIN | POLLPRI;
145 if (poll (fds, 1, 1000) <= 0)
148 memset (&ctlbuf, 0, sizeof (ctlbuf));
149 memset (&databuf, 0, sizeof (databuf));
150 ctlbuf.maxlen = sizeof (dlpi_ctl);
151 ctlbuf.buf = (void *)dlpi_ctl;
152 databuf.maxlen = sizeof (sock_buff);
153 databuf.buf = (void *)sock_buff;
155 retv = getmsg (fd, &ctlbuf, &databuf, &flags);
160 while (ctlbuf.len == 0);
162 if (!(retv & MORECTL))
164 while (retv & MOREDATA)
167 retv = getmsg (fd, NULL, &databuf, &flags);
172 while (retv & MORECTL)
175 retv = getmsg (fd, &ctlbuf, &databuf, &flags);
181 dlpiok (int fd, t_uscalar_t oprim)
184 dl_ok_ack_t *doa = (dl_ok_ack_t *)dlpi_ctl;
186 retv = dlpirctl (fd);
187 if (retv < (ssize_t)DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK ||
188 doa->dl_correct_primitive != oprim)
204 memset (&dir, 0, sizeof (dir));
205 dir.dl_primitive = DL_INFO_REQ;
206 /* Info_req uses M_PCPROTO. */
207 dlpisend (fd, &dir, sizeof (dir), NULL, 0, RS_HIPRI);
208 retv = dlpirctl (fd);
209 if (retv < (ssize_t)DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK)
216 dlpiopen (const char *devpath, ssize_t *acklen)
220 fd = open (devpath, O_RDWR | O_NONBLOCK | O_NOCTTY);
224 /* All that we want is for the open itself to be non-blocking, not I/O. */
225 flags = fcntl (fd, F_GETFL, 0);
227 fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
229 /* After opening, ask for information */
230 if ((*acklen = dlpiinfo (fd)) == -1)
240 dlpiattach (int fd, int unit)
244 memset (&dar, 0, sizeof (dar));
245 dar.dl_primitive = DL_ATTACH_REQ;
247 dlpisend (fd, &dar, sizeof (dar), NULL, 0, 0);
248 return dlpiok (fd, dar.dl_primitive);
256 dl_bind_ack_t *dba = (dl_bind_ack_t *)dlpi_ctl;
258 memset (&dbr, 0, sizeof (dbr));
259 dbr.dl_primitive = DL_BIND_REQ;
260 dbr.dl_service_mode = DL_CLDLS;
261 dlpisend (fd, &dbr, sizeof (dbr), NULL, 0, 0);
263 retv = dlpirctl (fd);
264 if (retv < (ssize_t)DL_BIND_ACK_SIZE || dba->dl_primitive != DL_BIND_ACK)
271 dlpimcast (int fd, const u_char *mcaddr)
274 dl_enabmulti_req_t der;
275 u_char addr[ETHERADDRL];
278 memset (&dler, 0, sizeof (dler));
279 dler.der.dl_primitive = DL_ENABMULTI_REQ;
280 dler.der.dl_addr_length = sizeof (dler.addr);
281 dler.der.dl_addr_offset = dler.addr - (u_char *)&dler;
282 memcpy (dler.addr, mcaddr, sizeof (dler.addr));
283 dlpisend (fd, &dler, sizeof (dler), NULL, 0, 0);
284 return dlpiok (fd, dler.der.dl_primitive);
288 dlpiaddr (int fd, u_char *addr)
290 dl_phys_addr_req_t dpar;
291 dl_phys_addr_ack_t *dpaa = (dl_phys_addr_ack_t *)dlpi_ctl;
294 memset (&dpar, 0, sizeof (dpar));
295 dpar.dl_primitive = DL_PHYS_ADDR_REQ;
296 dpar.dl_addr_type = DL_CURR_PHYS_ADDR;
297 dlpisend (fd, &dpar, sizeof (dpar), NULL, 0, 0);
299 retv = dlpirctl (fd);
300 if (retv < (ssize_t)DL_PHYS_ADDR_ACK_SIZE
301 || dpaa->dl_primitive != DL_PHYS_ADDR_ACK)
304 if (dpaa->dl_addr_offset < DL_PHYS_ADDR_ACK_SIZE ||
305 dpaa->dl_addr_length != ETHERADDRL ||
306 dpaa->dl_addr_offset + dpaa->dl_addr_length > (size_t)retv)
309 bcopy((char *)dpaa + dpaa->dl_addr_offset, addr, ETHERADDRL);
314 open_dlpi_dev (struct isis_circuit *circuit)
316 int fd = -1, unit, retval;
317 char devpath[MAXPATHLEN];
318 dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl;
321 /* Only broadcast-type are supported at the moment */
322 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
324 zlog_warn ("%s: non-broadcast interface %s", __func__,
325 circuit->interface->name);
329 /* Try the vanity node first, if permitted */
330 if (getenv("DLPI_DEVONLY") == NULL)
332 (void) snprintf (devpath, sizeof(devpath), "/dev/net/%s",
333 circuit->interface->name);
334 fd = dlpiopen (devpath, &acklen);
337 /* Now try as an ordinary Style 1 node */
340 (void) snprintf (devpath, sizeof (devpath), "/dev/%s",
341 circuit->interface->name);
343 fd = dlpiopen (devpath, &acklen);
346 /* If that fails, try again as Style 2 */
351 cp = devpath + strlen (devpath);
352 while (--cp >= devpath && isdigit(*cp))
354 unit = strtol(cp, NULL, 0);
356 fd = dlpiopen (devpath, &acklen);
358 /* If that too fails, then the device really doesn't exist */
361 zlog_warn ("%s: unknown interface %s", __func__,
362 circuit->interface->name);
366 /* Double check the DLPI style */
367 if (dia->dl_provider_style != DL_STYLE2)
369 zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 2",
370 circuit->interface->name, devpath);
375 /* If it succeeds, then we need to attach to the unit specified */
376 dlpiattach (fd, unit);
378 /* Reget the information, as it may be different per node */
379 if ((acklen = dlpiinfo (fd)) == -1)
387 /* Double check the DLPI style */
388 if (dia->dl_provider_style != DL_STYLE1)
390 zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 1",
391 circuit->interface->name, devpath);
397 /* Check that the interface we've got is the kind we expect */
398 if ((dia->dl_sap_length != 2 && dia->dl_sap_length != -2) ||
399 dia->dl_service_mode != DL_CLDLS || dia->dl_addr_length != ETHERADDRL + 2 ||
400 dia->dl_brdcst_addr_length != ETHERADDRL)
402 zlog_warn ("%s: unsupported interface type for %s", __func__,
403 circuit->interface->name);
407 switch (dia->dl_mac_type)
417 zlog_warn ("%s: unexpected mac type on %s: %lld", __func__,
418 circuit->interface->name, (long long)dia->dl_mac_type);
423 circuit->sap_length = dia->dl_sap_length;
426 * The local hardware address is something that should be provided by way of
427 * sockaddr_dl for the interface, but isn't on Solaris. We set it here based
428 * on DLPI's reported address to avoid roto-tilling the world.
429 * (Note that isis_circuit_if_add on Solaris doesn't set the snpa.)
431 * Unfortunately, GLD is broken and doesn't provide the address after attach,
432 * so we need to be careful and use DL_PHYS_ADDR_REQ instead.
434 if (dlpiaddr (fd, circuit->u.bc.snpa) == -1)
436 zlog_warn ("open_dlpi_dev(): interface %s: unable to get MAC address",
437 circuit->interface->name);
442 /* Now bind to SAP 0. This gives us 802-type traffic. */
443 if (dlpibind (fd) == -1)
445 zlog_warn ("%s: cannot bind SAP 0 on %s", __func__,
446 circuit->interface->name);
452 * Join to multicast groups according to
453 * 8.4.2 - Broadcast subnetwork IIH PDUs
456 retval |= dlpimcast (fd, ALL_L1_ISS);
457 retval |= dlpimcast (fd, ALL_ISS);
458 retval |= dlpimcast (fd, ALL_L2_ISS);
462 zlog_warn ("%s: unable to join multicast on %s", __func__,
463 circuit->interface->name);
468 /* Push on the packet filter to avoid stray 802 packets */
469 if (ioctl (fd, I_PUSH, "pfmod") == 0)
471 struct packetfilt pfil;
472 struct strioctl sioc;
474 pfil.Pf_Priority = 0;
475 pfil.Pf_FilterLen = sizeof (pf_filter) / sizeof (u_short);
476 memcpy (pfil.Pf_Filter, pf_filter, sizeof (pf_filter));
477 /* pfmod does not support transparent ioctls */
478 sioc.ic_cmd = PFIOCSETF;
480 sioc.ic_len = sizeof (struct packetfilt);
481 sioc.ic_dp = (char *)&pfil;
482 if (ioctl (fd, I_STR, &sioc) == -1)
483 zlog_warn("%s: could not perform PF_IOCSETF on %s",
484 __func__, circuit->interface->name);
493 * Create the socket and set the tx/rx funcs
496 isis_sock_init (struct isis_circuit *circuit)
498 int retval = ISIS_OK;
500 if (isisd_privs.change (ZPRIVS_RAISE))
501 zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
503 retval = open_dlpi_dev (circuit);
505 if (retval != ISIS_OK)
507 zlog_warn ("%s: could not initialize the socket", __func__);
511 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
513 circuit->tx = isis_send_pdu_bcast;
514 circuit->rx = isis_recv_pdu_bcast;
518 zlog_warn ("isis_sock_init(): unknown circuit type");
519 retval = ISIS_WARNING;
524 if (isisd_privs.change (ZPRIVS_LOWER))
525 zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
531 isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
533 struct pollfd fds[1];
534 struct strbuf ctlbuf, databuf;
536 dl_unitdata_ind_t *dui = (dl_unitdata_ind_t *)dlpi_ctl;
538 memset (fds, 0, sizeof (fds));
539 fds[0].fd = circuit->fd;
540 fds[0].events = POLLIN | POLLPRI;
541 if (poll (fds, 1, 0) <= 0)
544 memset (&ctlbuf, 0, sizeof (ctlbuf));
545 memset (&databuf, 0, sizeof (databuf));
546 ctlbuf.maxlen = sizeof (dlpi_ctl);
547 ctlbuf.buf = (void *)dlpi_ctl;
548 databuf.maxlen = sizeof (sock_buff);
549 databuf.buf = (void *)sock_buff;
551 retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags);
555 zlog_warn ("isis_recv_pdu_bcast: getmsg failed: %s",
556 safe_strerror (errno));
560 if (retv & (MORECTL | MOREDATA))
562 while (retv & (MORECTL | MOREDATA))
565 retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags);
570 if (ctlbuf.len < (ssize_t)DL_UNITDATA_IND_SIZE ||
571 dui->dl_primitive != DL_UNITDATA_IND)
574 if (dui->dl_src_addr_length != ETHERADDRL + 2 ||
575 dui->dl_src_addr_offset < DL_UNITDATA_IND_SIZE ||
576 dui->dl_src_addr_offset + dui->dl_src_addr_length > (size_t)ctlbuf.len)
579 memcpy (ssnpa, (char *)dui + dui->dl_src_addr_offset +
580 (circuit->sap_length > 0 ? circuit->sap_length : 0), ETHERADDRL);
582 if (databuf.len < LLC_LEN || sock_buff[0] != ISO_SAP ||
583 sock_buff[1] != ISO_SAP || sock_buff[2] != 3)
586 stream_write (circuit->rcv_stream, sock_buff + LLC_LEN,
587 databuf.len - LLC_LEN);
588 stream_set_getp (circuit->rcv_stream, 0);
594 isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
596 dl_unitdata_req_t *dur = (dl_unitdata_req_t *)dlpi_ctl;
602 buflen = stream_get_endp (circuit->snd_stream) + LLC_LEN;
603 if ((size_t)buflen > sizeof (sock_buff))
605 zlog_warn ("isis_send_pdu_bcast: sock_buff size %zu is less than "
606 "output pdu size %d on circuit %s",
607 sizeof (sock_buff), buflen, circuit->interface->name);
611 stream_set_getp (circuit->snd_stream, 0);
613 memset (dur, 0, sizeof (*dur));
614 dur->dl_primitive = DL_UNITDATA_REQ;
615 dur->dl_dest_addr_length = ETHERADDRL + 2;
616 dur->dl_dest_addr_offset = sizeof (*dur);
618 dstaddr = (char *)(dur + 1);
619 if (circuit->sap_length < 0)
621 dstsap = (u_short *)(dstaddr + ETHERADDRL);
625 dstsap = (u_short *)dstaddr;
626 dstaddr += circuit->sap_length;
629 memcpy (dstaddr, ALL_L1_ISS, ETHERADDRL);
631 memcpy (dstaddr, ALL_L2_ISS, ETHERADDRL);
632 /* Note: DLPI SAP values are in host byte order */
635 sock_buff[0] = ISO_SAP;
636 sock_buff[1] = ISO_SAP;
638 memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
639 stream_get_endp (circuit->snd_stream));
640 rv = dlpisend(circuit->fd, dur, sizeof (*dur) + dur->dl_dest_addr_length,
641 sock_buff, buflen, 0);
644 zlog_warn("IS-IS dlpi: could not transmit packet on %s: %s",
645 circuit->interface->name, safe_strerror(errno));
646 if (ERRNO_IO_RETRY(errno))
654 #endif /* ISIS_METHOD == ISIS_METHOD_DLPI */