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 */