]>
git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_dlpi.c
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; see the file COPYING; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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/isis_constants.h"
42 #include "isisd/isis_common.h"
43 #include "isisd/isis_circuit.h"
44 #include "isisd/isis_flags.h"
45 #include "isisd/isisd.h"
46 #include "isisd/isis_network.h"
50 static t_uscalar_t dlpi_ctl
[1024]; /* DLPI control messages */
53 * Table 9 - Architectural constants for use with ISO 8802 subnetworks
57 u_char ALL_L1_ISS
[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14};
58 u_char ALL_L2_ISS
[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15};
59 u_char ALL_ISS
[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05};
60 u_char ALL_ESS
[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04};
62 static u_char sock_buff
[8192];
64 static u_short pf_filter
[] = {
65 ENF_PUSHWORD
+ 0, /* Get the SSAP/DSAP values */
66 ENF_PUSHLIT
| ENF_CAND
, /* Check them */
67 ISO_SAP
| (ISO_SAP
<< 8),
68 ENF_PUSHWORD
+ 1, /* Get the control value */
69 ENF_PUSHLIT
| ENF_AND
, /* Isolate it */
75 ENF_PUSHLIT
| ENF_CAND
, /* Test for expected value */
84 * We would like to use something like libdlpi here, but that's not present on
85 * all versions of Solaris or on any non-Solaris system, so it's nowhere near
86 * as portable as we'd like. Thus, we use the standards-conformant DLPI
87 * interfaces plus the (optional; not needed) Solaris packet filter module.
90 static int dlpisend(int fd
, const void *cbuf
, size_t cbuflen
, const void *dbuf
,
91 size_t dbuflen
, int flags
)
93 const struct strbuf
*ctlptr
= NULL
;
94 const struct strbuf
*dataptr
= NULL
;
95 struct strbuf ctlbuf
, databuf
;
99 memset(&ctlbuf
, 0, sizeof(ctlbuf
));
100 ctlbuf
.len
= cbuflen
;
101 ctlbuf
.buf
= (void *)cbuf
;
106 memset(&databuf
, 0, sizeof(databuf
));
107 databuf
.len
= dbuflen
;
108 databuf
.buf
= (void *)dbuf
;
112 /* We assume this doesn't happen often and isn't operationally
114 rv
= putmsg(fd
, ctlptr
, dataptr
, flags
);
115 if (rv
== -1 && dbuf
== NULL
) {
117 * For actual PDU transmission - recognizable buf dbuf != NULL,
118 * the error is passed upwards and should not be printed here.
120 zlog_debug("%s: putmsg: %s", __func__
, safe_strerror(errno
));
125 static ssize_t
dlpirctl(int fd
)
127 struct pollfd fds
[1];
128 struct strbuf ctlbuf
, databuf
;
132 /* Poll is used here in case the device doesn't speak DLPI
134 memset(fds
, 0, sizeof(fds
));
136 fds
[0].events
= POLLIN
| POLLPRI
;
137 if (poll(fds
, 1, 1000) <= 0)
140 memset(&ctlbuf
, 0, sizeof(ctlbuf
));
141 memset(&databuf
, 0, sizeof(databuf
));
142 ctlbuf
.maxlen
= sizeof(dlpi_ctl
);
143 ctlbuf
.buf
= (void *)dlpi_ctl
;
144 databuf
.maxlen
= sizeof(sock_buff
);
145 databuf
.buf
= (void *)sock_buff
;
147 retv
= getmsg(fd
, &ctlbuf
, &databuf
, &flags
);
151 } while (ctlbuf
.len
== 0);
153 if (!(retv
& MORECTL
)) {
154 while (retv
& MOREDATA
) {
156 retv
= getmsg(fd
, NULL
, &databuf
, &flags
);
161 while (retv
& MORECTL
) {
163 retv
= getmsg(fd
, &ctlbuf
, &databuf
, &flags
);
168 static int dlpiok(int fd
, t_uscalar_t oprim
)
171 dl_ok_ack_t
*doa
= (dl_ok_ack_t
*)dlpi_ctl
;
174 if (retv
< (ssize_t
)DL_OK_ACK_SIZE
|| doa
->dl_primitive
!= DL_OK_ACK
175 || doa
->dl_correct_primitive
!= oprim
) {
182 static int dlpiinfo(int fd
)
187 memset(&dir
, 0, sizeof(dir
));
188 dir
.dl_primitive
= DL_INFO_REQ
;
189 /* Info_req uses M_PCPROTO. */
190 dlpisend(fd
, &dir
, sizeof(dir
), NULL
, 0, RS_HIPRI
);
192 if (retv
< (ssize_t
)DL_INFO_ACK_SIZE
|| dlpi_ctl
[0] != DL_INFO_ACK
)
198 static int dlpiopen(const char *devpath
, ssize_t
*acklen
)
202 fd
= open(devpath
, O_RDWR
| O_NONBLOCK
| O_NOCTTY
);
206 /* All that we want is for the open itself to be non-blocking, not I/O.
208 flags
= fcntl(fd
, F_GETFL
, 0);
210 fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
212 /* After opening, ask for information */
213 if ((*acklen
= dlpiinfo(fd
)) == -1) {
221 static int dlpiattach(int fd
, int unit
)
225 memset(&dar
, 0, sizeof(dar
));
226 dar
.dl_primitive
= DL_ATTACH_REQ
;
228 dlpisend(fd
, &dar
, sizeof(dar
), NULL
, 0, 0);
229 return dlpiok(fd
, dar
.dl_primitive
);
232 static int dlpibind(int fd
)
236 dl_bind_ack_t
*dba
= (dl_bind_ack_t
*)dlpi_ctl
;
238 memset(&dbr
, 0, sizeof(dbr
));
239 dbr
.dl_primitive
= DL_BIND_REQ
;
240 dbr
.dl_service_mode
= DL_CLDLS
;
241 dlpisend(fd
, &dbr
, sizeof(dbr
), NULL
, 0, 0);
244 if (retv
< (ssize_t
)DL_BIND_ACK_SIZE
245 || dba
->dl_primitive
!= DL_BIND_ACK
)
251 static int dlpimcast(int fd
, const u_char
*mcaddr
)
254 dl_enabmulti_req_t der
;
255 u_char addr
[ETHERADDRL
];
258 memset(&dler
, 0, sizeof(dler
));
259 dler
.der
.dl_primitive
= DL_ENABMULTI_REQ
;
260 dler
.der
.dl_addr_length
= sizeof(dler
.addr
);
261 dler
.der
.dl_addr_offset
= dler
.addr
- (u_char
*)&dler
;
262 memcpy(dler
.addr
, mcaddr
, sizeof(dler
.addr
));
263 dlpisend(fd
, &dler
, sizeof(dler
), NULL
, 0, 0);
264 return dlpiok(fd
, dler
.der
.dl_primitive
);
267 static int dlpiaddr(int fd
, u_char
*addr
)
269 dl_phys_addr_req_t dpar
;
270 dl_phys_addr_ack_t
*dpaa
= (dl_phys_addr_ack_t
*)dlpi_ctl
;
273 memset(&dpar
, 0, sizeof(dpar
));
274 dpar
.dl_primitive
= DL_PHYS_ADDR_REQ
;
275 dpar
.dl_addr_type
= DL_CURR_PHYS_ADDR
;
276 dlpisend(fd
, &dpar
, sizeof(dpar
), NULL
, 0, 0);
279 if (retv
< (ssize_t
)DL_PHYS_ADDR_ACK_SIZE
280 || dpaa
->dl_primitive
!= DL_PHYS_ADDR_ACK
)
283 if (dpaa
->dl_addr_offset
< DL_PHYS_ADDR_ACK_SIZE
284 || dpaa
->dl_addr_length
!= ETHERADDRL
285 || dpaa
->dl_addr_offset
+ dpaa
->dl_addr_length
> (size_t)retv
)
288 bcopy((char *)dpaa
+ dpaa
->dl_addr_offset
, addr
, ETHERADDRL
);
292 static int open_dlpi_dev(struct isis_circuit
*circuit
)
294 int fd
= -1, unit
, retval
;
295 char devpath
[MAXPATHLEN
];
296 dl_info_ack_t
*dia
= (dl_info_ack_t
*)dlpi_ctl
;
299 /* Only broadcast-type are supported at the moment */
300 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
301 zlog_warn("%s: non-broadcast interface %s", __func__
,
302 circuit
->interface
->name
);
306 /* Try the vanity node first, if permitted */
307 if (getenv("DLPI_DEVONLY") == NULL
) {
308 (void)snprintf(devpath
, sizeof(devpath
), "/dev/net/%s",
309 circuit
->interface
->name
);
310 fd
= dlpiopen(devpath
, &acklen
);
313 /* Now try as an ordinary Style 1 node */
315 (void)snprintf(devpath
, sizeof(devpath
), "/dev/%s",
316 circuit
->interface
->name
);
318 fd
= dlpiopen(devpath
, &acklen
);
321 /* If that fails, try again as Style 2 */
325 cp
= devpath
+ strlen(devpath
);
326 while (--cp
>= devpath
&& isdigit(*cp
))
328 unit
= strtol(cp
, NULL
, 0);
330 fd
= dlpiopen(devpath
, &acklen
);
332 /* If that too fails, then the device really doesn't exist */
334 zlog_warn("%s: unknown interface %s", __func__
,
335 circuit
->interface
->name
);
339 /* Double check the DLPI style */
340 if (dia
->dl_provider_style
!= DL_STYLE2
) {
342 "open_dlpi_dev(): interface %s: %s is not style 2",
343 circuit
->interface
->name
, devpath
);
348 /* If it succeeds, then we need to attach to the unit specified
350 dlpiattach(fd
, unit
);
352 /* Reget the information, as it may be different per node */
353 if ((acklen
= dlpiinfo(fd
)) == -1) {
358 /* Double check the DLPI style */
359 if (dia
->dl_provider_style
!= DL_STYLE1
) {
361 "open_dlpi_dev(): interface %s: %s is not style 1",
362 circuit
->interface
->name
, devpath
);
368 /* Check that the interface we've got is the kind we expect */
369 if ((dia
->dl_sap_length
!= 2 && dia
->dl_sap_length
!= -2)
370 || dia
->dl_service_mode
!= DL_CLDLS
371 || dia
->dl_addr_length
!= ETHERADDRL
+ 2
372 || dia
->dl_brdcst_addr_length
!= ETHERADDRL
) {
373 zlog_warn("%s: unsupported interface type for %s", __func__
,
374 circuit
->interface
->name
);
378 switch (dia
->dl_mac_type
) {
387 zlog_warn("%s: unexpected mac type on %s: %lld", __func__
,
388 circuit
->interface
->name
,
389 (long long)dia
->dl_mac_type
);
394 circuit
->sap_length
= dia
->dl_sap_length
;
397 * The local hardware address is something that should be provided by
399 * sockaddr_dl for the interface, but isn't on Solaris. We set it here
401 * on DLPI's reported address to avoid roto-tilling the world.
402 * (Note that isis_circuit_if_add on Solaris doesn't set the snpa.)
404 * Unfortunately, GLD is broken and doesn't provide the address after
406 * so we need to be careful and use DL_PHYS_ADDR_REQ instead.
408 if (dlpiaddr(fd
, circuit
->u
.bc
.snpa
) == -1) {
410 "open_dlpi_dev(): interface %s: unable to get MAC address",
411 circuit
->interface
->name
);
416 /* Now bind to SAP 0. This gives us 802-type traffic. */
417 if (dlpibind(fd
) == -1) {
418 zlog_warn("%s: cannot bind SAP 0 on %s", __func__
,
419 circuit
->interface
->name
);
425 * Join to multicast groups according to
426 * 8.4.2 - Broadcast subnetwork IIH PDUs
429 retval
|= dlpimcast(fd
, ALL_L1_ISS
);
430 retval
|= dlpimcast(fd
, ALL_ISS
);
431 retval
|= dlpimcast(fd
, ALL_L2_ISS
);
434 zlog_warn("%s: unable to join multicast on %s", __func__
,
435 circuit
->interface
->name
);
440 /* Push on the packet filter to avoid stray 802 packets */
441 if (ioctl(fd
, I_PUSH
, "pfmod") == 0) {
442 struct packetfilt pfil
;
443 struct strioctl sioc
;
445 pfil
.Pf_Priority
= 0;
446 pfil
.Pf_FilterLen
= sizeof(pf_filter
) / sizeof(u_short
);
447 memcpy(pfil
.Pf_Filter
, pf_filter
, sizeof(pf_filter
));
448 /* pfmod does not support transparent ioctls */
449 sioc
.ic_cmd
= PFIOCSETF
;
451 sioc
.ic_len
= sizeof(struct packetfilt
);
452 sioc
.ic_dp
= (char *)&pfil
;
453 if (ioctl(fd
, I_STR
, &sioc
) == -1)
454 zlog_warn("%s: could not perform PF_IOCSETF on %s",
455 __func__
, circuit
->interface
->name
);
464 * Create the socket and set the tx/rx funcs
466 int isis_sock_init(struct isis_circuit
*circuit
)
468 int retval
= ISIS_OK
;
470 if (isisd_privs
.change(ZPRIVS_RAISE
))
471 zlog_err("%s: could not raise privs, %s", __func__
,
472 safe_strerror(errno
));
474 retval
= open_dlpi_dev(circuit
);
476 if (retval
!= ISIS_OK
) {
477 zlog_warn("%s: could not initialize the socket", __func__
);
481 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
482 circuit
->tx
= isis_send_pdu_bcast
;
483 circuit
->rx
= isis_recv_pdu_bcast
;
485 zlog_warn("isis_sock_init(): unknown circuit type");
486 retval
= ISIS_WARNING
;
491 if (isisd_privs
.change(ZPRIVS_LOWER
))
492 zlog_err("%s: could not lower privs, %s", __func__
,
493 safe_strerror(errno
));
498 int isis_recv_pdu_bcast(struct isis_circuit
*circuit
, u_char
*ssnpa
)
500 struct pollfd fds
[1];
501 struct strbuf ctlbuf
, databuf
;
503 dl_unitdata_ind_t
*dui
= (dl_unitdata_ind_t
*)dlpi_ctl
;
505 memset(fds
, 0, sizeof(fds
));
506 fds
[0].fd
= circuit
->fd
;
507 fds
[0].events
= POLLIN
| POLLPRI
;
508 if (poll(fds
, 1, 0) <= 0)
511 memset(&ctlbuf
, 0, sizeof(ctlbuf
));
512 memset(&databuf
, 0, sizeof(databuf
));
513 ctlbuf
.maxlen
= sizeof(dlpi_ctl
);
514 ctlbuf
.buf
= (void *)dlpi_ctl
;
515 databuf
.maxlen
= sizeof(sock_buff
);
516 databuf
.buf
= (void *)sock_buff
;
518 retv
= getmsg(circuit
->fd
, &ctlbuf
, &databuf
, &flags
);
521 zlog_warn("isis_recv_pdu_bcast: getmsg failed: %s",
522 safe_strerror(errno
));
526 if (retv
& (MORECTL
| MOREDATA
)) {
527 while (retv
& (MORECTL
| MOREDATA
)) {
529 retv
= getmsg(circuit
->fd
, &ctlbuf
, &databuf
, &flags
);
534 if (ctlbuf
.len
< (ssize_t
)DL_UNITDATA_IND_SIZE
535 || dui
->dl_primitive
!= DL_UNITDATA_IND
)
538 if (dui
->dl_src_addr_length
!= ETHERADDRL
+ 2
539 || dui
->dl_src_addr_offset
< DL_UNITDATA_IND_SIZE
540 || dui
->dl_src_addr_offset
+ dui
->dl_src_addr_length
541 > (size_t)ctlbuf
.len
)
545 (char *)dui
+ dui
->dl_src_addr_offset
546 + (circuit
->sap_length
> 0 ? circuit
->sap_length
: 0),
549 if (databuf
.len
< LLC_LEN
|| sock_buff
[0] != ISO_SAP
550 || sock_buff
[1] != ISO_SAP
|| sock_buff
[2] != 3)
553 stream_write(circuit
->rcv_stream
, sock_buff
+ LLC_LEN
,
554 databuf
.len
- LLC_LEN
);
555 stream_set_getp(circuit
->rcv_stream
, 0);
560 int isis_send_pdu_bcast(struct isis_circuit
*circuit
, int level
)
562 dl_unitdata_req_t
*dur
= (dl_unitdata_req_t
*)dlpi_ctl
;
568 buflen
= stream_get_endp(circuit
->snd_stream
) + LLC_LEN
;
569 if ((size_t)buflen
> sizeof(sock_buff
)) {
571 "isis_send_pdu_bcast: sock_buff size %zu is less than "
572 "output pdu size %d on circuit %s",
573 sizeof(sock_buff
), buflen
, circuit
->interface
->name
);
577 stream_set_getp(circuit
->snd_stream
, 0);
579 memset(dur
, 0, sizeof(*dur
));
580 dur
->dl_primitive
= DL_UNITDATA_REQ
;
581 dur
->dl_dest_addr_length
= ETHERADDRL
+ 2;
582 dur
->dl_dest_addr_offset
= sizeof(*dur
);
584 dstaddr
= (char *)(dur
+ 1);
585 if (circuit
->sap_length
< 0) {
586 dstsap
= (u_short
*)(dstaddr
+ ETHERADDRL
);
588 dstsap
= (u_short
*)dstaddr
;
589 dstaddr
+= circuit
->sap_length
;
592 memcpy(dstaddr
, ALL_L1_ISS
, ETHERADDRL
);
594 memcpy(dstaddr
, ALL_L2_ISS
, ETHERADDRL
);
595 /* Note: DLPI SAP values are in host byte order */
598 sock_buff
[0] = ISO_SAP
;
599 sock_buff
[1] = ISO_SAP
;
601 memcpy(sock_buff
+ LLC_LEN
, circuit
->snd_stream
->data
,
602 stream_get_endp(circuit
->snd_stream
));
603 rv
= dlpisend(circuit
->fd
, dur
, sizeof(*dur
) + dur
->dl_dest_addr_length
,
604 sock_buff
, buflen
, 0);
606 zlog_warn("IS-IS dlpi: could not transmit packet on %s: %s",
607 circuit
->interface
->name
, safe_strerror(errno
));
608 if (ERRNO_IO_RETRY(errno
))
616 #endif /* ISIS_METHOD == ISIS_METHOD_DLPI */