]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_dlpi.c
Merge pull request #5280 from qlyoung/doc-clean-topotest-json
[mirror_frr.git] / isisd / isis_dlpi.c
CommitLineData
8bc98059
PJ
1/*
2 * IS-IS Rout(e)ing protocol - isis_dlpi.c
3 *
4 * Copyright (C) 2001,2002 Sampo Saaristo
d62a17ae 5 * Tampere University of Technology
8bc98059
PJ
6 * Institute of Communications Engineering
7 *
d62a17ae 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)
8bc98059
PJ
11 * any later version.
12 *
d62a17ae 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
8bc98059 16 * more details.
896014f4
DL
17 *
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
8bc98059
PJ
21 */
22
23#include <zebra.h>
745bf05f 24#if ISIS_METHOD == ISIS_METHOD_DLPI
8bc98059
PJ
25#include <net/if.h>
26#include <netinet/if_ether.h>
27#include <sys/types.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <stropts.h>
31#include <poll.h>
32#include <sys/dlpi.h>
33#include <sys/pfmod.h>
34
35#include "log.h"
cfd1f27b 36#include "network.h"
8bc98059
PJ
37#include "stream.h"
38#include "if.h"
38937bd5 39#include "lib_errors.h"
8bc98059 40
8bc98059
PJ
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"
8bc98059
PJ
46#include "isisd/isis_network.h"
47
48#include "privs.h"
49
d62a17ae 50static t_uscalar_t dlpi_ctl[1024]; /* DLPI control messages */
8bc98059
PJ
51
52/*
53 * Table 9 - Architectural constants for use with ISO 8802 subnetworks
54 * ISO 10589 - 8.4.8
55 */
56
d7c0a89a
QY
57uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14};
58uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15};
59uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05};
60uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04};
8bc98059 61
d7c0a89a 62static uint8_t sock_buff[8192];
8bc98059 63
d7c0a89a 64static unsigned short pf_filter[] = {
d62a17ae 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 */
8bc98059 70#ifdef _BIG_ENDIAN
d62a17ae 71 0xFF00,
8bc98059 72#else
d62a17ae 73 0x00FF,
8bc98059 74#endif
d62a17ae 75 ENF_PUSHLIT | ENF_CAND, /* Test for expected value */
8bc98059 76#ifdef _BIG_ENDIAN
d62a17ae 77 0x0300
8bc98059 78#else
d62a17ae 79 0x0003
8bc98059
PJ
80#endif
81};
82
83/*
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.
88 */
89
d62a17ae 90static int dlpisend(int fd, const void *cbuf, size_t cbuflen, const void *dbuf,
91 size_t dbuflen, int flags)
8bc98059 92{
d62a17ae 93 const struct strbuf *ctlptr = NULL;
94 const struct strbuf *dataptr = NULL;
95 struct strbuf ctlbuf, databuf;
96 int rv;
97
98 if (cbuf != NULL) {
99 memset(&ctlbuf, 0, sizeof(ctlbuf));
100 ctlbuf.len = cbuflen;
101 ctlbuf.buf = (void *)cbuf;
102 ctlptr = &ctlbuf;
103 }
104
105 if (dbuf != NULL) {
106 memset(&databuf, 0, sizeof(databuf));
107 databuf.len = dbuflen;
108 databuf.buf = (void *)dbuf;
109 dataptr = &databuf;
110 }
111
112 /* We assume this doesn't happen often and isn't operationally
113 * significant */
114 rv = putmsg(fd, ctlptr, dataptr, flags);
115 if (rv == -1 && dbuf == NULL) {
116 /*
117 * For actual PDU transmission - recognizable buf dbuf != NULL,
118 * the error is passed upwards and should not be printed here.
119 */
120 zlog_debug("%s: putmsg: %s", __func__, safe_strerror(errno));
121 }
122 return rv;
8bc98059
PJ
123}
124
d62a17ae 125static ssize_t dlpirctl(int fd)
8bc98059 126{
d62a17ae 127 struct pollfd fds[1];
128 struct strbuf ctlbuf, databuf;
129 int flags, retv;
130
131 do {
132 /* Poll is used here in case the device doesn't speak DLPI
133 * correctly */
134 memset(fds, 0, sizeof(fds));
135 fds[0].fd = fd;
136 fds[0].events = POLLIN | POLLPRI;
137 if (poll(fds, 1, 1000) <= 0)
138 return -1;
139
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;
146 flags = 0;
147 retv = getmsg(fd, &ctlbuf, &databuf, &flags);
148
149 if (retv < 0)
150 return -1;
151 } while (ctlbuf.len == 0);
152
153 if (!(retv & MORECTL)) {
154 while (retv & MOREDATA) {
155 flags = 0;
156 retv = getmsg(fd, NULL, &databuf, &flags);
157 }
158 return ctlbuf.len;
159 }
8bc98059 160
d62a17ae 161 while (retv & MORECTL) {
162 flags = 0;
163 retv = getmsg(fd, &ctlbuf, &databuf, &flags);
8bc98059 164 }
d62a17ae 165 return -1;
8bc98059
PJ
166}
167
d62a17ae 168static int dlpiok(int fd, t_uscalar_t oprim)
8bc98059 169{
d62a17ae 170 int retv;
171 dl_ok_ack_t *doa = (dl_ok_ack_t *)dlpi_ctl;
172
173 retv = dlpirctl(fd);
174 if (retv < (ssize_t)DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK
175 || doa->dl_correct_primitive != oprim) {
176 return -1;
177 } else {
178 return 0;
179 }
8bc98059
PJ
180}
181
d62a17ae 182static int dlpiinfo(int fd)
8bc98059 183{
d62a17ae 184 dl_info_req_t dir;
185 ssize_t retv;
186
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);
191 retv = dlpirctl(fd);
192 if (retv < (ssize_t)DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK)
193 return -1;
194 else
195 return retv;
8bc98059
PJ
196}
197
d62a17ae 198static int dlpiopen(const char *devpath, ssize_t *acklen)
8bc98059 199{
d62a17ae 200 int fd, flags;
201
202 fd = open(devpath, O_RDWR | O_NONBLOCK | O_NOCTTY);
203 if (fd == -1)
204 return -1;
205
206 /* All that we want is for the open itself to be non-blocking, not I/O.
207 */
208 flags = fcntl(fd, F_GETFL, 0);
209 if (flags != -1)
210 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
211
212 /* After opening, ask for information */
213 if ((*acklen = dlpiinfo(fd)) == -1) {
214 close(fd);
215 return -1;
216 }
8bc98059 217
d62a17ae 218 return fd;
8bc98059
PJ
219}
220
d62a17ae 221static int dlpiattach(int fd, int unit)
8bc98059 222{
d62a17ae 223 dl_attach_req_t dar;
8bc98059 224
d62a17ae 225 memset(&dar, 0, sizeof(dar));
226 dar.dl_primitive = DL_ATTACH_REQ;
227 dar.dl_ppa = unit;
228 dlpisend(fd, &dar, sizeof(dar), NULL, 0, 0);
229 return dlpiok(fd, dar.dl_primitive);
8bc98059
PJ
230}
231
d62a17ae 232static int dlpibind(int fd)
8bc98059 233{
d62a17ae 234 dl_bind_req_t dbr;
235 int retv;
236 dl_bind_ack_t *dba = (dl_bind_ack_t *)dlpi_ctl;
237
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);
242
243 retv = dlpirctl(fd);
244 if (retv < (ssize_t)DL_BIND_ACK_SIZE
245 || dba->dl_primitive != DL_BIND_ACK)
246 return -1;
247 else
248 return 0;
8bc98059
PJ
249}
250
d7c0a89a 251static int dlpimcast(int fd, const uint8_t *mcaddr)
8bc98059 252{
d62a17ae 253 struct {
254 dl_enabmulti_req_t der;
d7c0a89a 255 uint8_t addr[ETHERADDRL];
d62a17ae 256 } dler;
257
258 memset(&dler, 0, sizeof(dler));
259 dler.der.dl_primitive = DL_ENABMULTI_REQ;
260 dler.der.dl_addr_length = sizeof(dler.addr);
d7c0a89a 261 dler.der.dl_addr_offset = dler.addr - (uint8_t *)&dler;
d62a17ae 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);
8bc98059
PJ
265}
266
d7c0a89a 267static int dlpiaddr(int fd, uint8_t *addr)
8bc98059 268{
d62a17ae 269 dl_phys_addr_req_t dpar;
270 dl_phys_addr_ack_t *dpaa = (dl_phys_addr_ack_t *)dlpi_ctl;
271 int retv;
272
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);
277
278 retv = dlpirctl(fd);
279 if (retv < (ssize_t)DL_PHYS_ADDR_ACK_SIZE
280 || dpaa->dl_primitive != DL_PHYS_ADDR_ACK)
281 return -1;
282
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)
286 return -1;
287
288 bcopy((char *)dpaa + dpaa->dl_addr_offset, addr, ETHERADDRL);
289 return 0;
8bc98059
PJ
290}
291
d62a17ae 292static int open_dlpi_dev(struct isis_circuit *circuit)
8bc98059 293{
d62a17ae 294 int fd = -1, unit, retval;
295 char devpath[MAXPATHLEN];
296 dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl;
297 ssize_t acklen;
298
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);
303 return ISIS_WARNING;
304 }
305
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);
311 }
312
313 /* Now try as an ordinary Style 1 node */
314 if (fd == -1) {
315 (void)snprintf(devpath, sizeof(devpath), "/dev/%s",
316 circuit->interface->name);
317 unit = -1;
318 fd = dlpiopen(devpath, &acklen);
319 }
320
321 /* If that fails, try again as Style 2 */
322 if (fd == -1) {
323 char *cp;
324
325 cp = devpath + strlen(devpath);
326 while (--cp >= devpath && isdigit(*cp))
327 ;
328 unit = strtol(cp, NULL, 0);
329 *cp = '\0';
330 fd = dlpiopen(devpath, &acklen);
331
332 /* If that too fails, then the device really doesn't exist */
333 if (fd == -1) {
334 zlog_warn("%s: unknown interface %s", __func__,
335 circuit->interface->name);
336 return ISIS_WARNING;
337 }
338
339 /* Double check the DLPI style */
340 if (dia->dl_provider_style != DL_STYLE2) {
341 zlog_warn(
342 "open_dlpi_dev(): interface %s: %s is not style 2",
343 circuit->interface->name, devpath);
344 close(fd);
345 return ISIS_WARNING;
346 }
347
348 /* If it succeeds, then we need to attach to the unit specified
349 */
350 dlpiattach(fd, unit);
351
352 /* Reget the information, as it may be different per node */
353 if ((acklen = dlpiinfo(fd)) == -1) {
354 close(fd);
355 return ISIS_WARNING;
356 }
357 } else {
358 /* Double check the DLPI style */
359 if (dia->dl_provider_style != DL_STYLE1) {
360 zlog_warn(
361 "open_dlpi_dev(): interface %s: %s is not style 1",
362 circuit->interface->name, devpath);
363 close(fd);
364 return ISIS_WARNING;
365 }
8bc98059
PJ
366 }
367
d62a17ae 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);
375 close(fd);
376 return ISIS_WARNING;
377 }
378 switch (dia->dl_mac_type) {
379 case DL_CSMACD:
380 case DL_ETHER:
381 case DL_100VG:
382 case DL_100VGTPR:
383 case DL_ETH_CSMA:
384 case DL_100BT:
385 break;
386 default:
387 zlog_warn("%s: unexpected mac type on %s: %lld", __func__,
388 circuit->interface->name,
389 (long long)dia->dl_mac_type);
390 close(fd);
391 return ISIS_WARNING;
392 }
393
394 circuit->sap_length = dia->dl_sap_length;
395
396 /*
397 * The local hardware address is something that should be provided by
398 * way of
399 * sockaddr_dl for the interface, but isn't on Solaris. We set it here
400 * based
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.)
403 *
404 * Unfortunately, GLD is broken and doesn't provide the address after
405 * attach,
406 * so we need to be careful and use DL_PHYS_ADDR_REQ instead.
407 */
408 if (dlpiaddr(fd, circuit->u.bc.snpa) == -1) {
409 zlog_warn(
410 "open_dlpi_dev(): interface %s: unable to get MAC address",
411 circuit->interface->name);
412 close(fd);
413 return ISIS_WARNING;
8bc98059
PJ
414 }
415
d62a17ae 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);
420 close(fd);
421 return ISIS_WARNING;
422 }
8bc98059 423
d62a17ae 424 /*
425 * Join to multicast groups according to
426 * 8.4.2 - Broadcast subnetwork IIH PDUs
427 */
428 retval = 0;
429 retval |= dlpimcast(fd, ALL_L1_ISS);
430 retval |= dlpimcast(fd, ALL_ISS);
431 retval |= dlpimcast(fd, ALL_L2_ISS);
432
433 if (retval != 0) {
434 zlog_warn("%s: unable to join multicast on %s", __func__,
435 circuit->interface->name);
436 close(fd);
437 return ISIS_WARNING;
8bc98059 438 }
d62a17ae 439
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;
444
445 pfil.Pf_Priority = 0;
97b5d752 446 pfil.Pf_FilterLen = array_size(pf_filter);
d62a17ae 447 memcpy(pfil.Pf_Filter, pf_filter, sizeof(pf_filter));
448 /* pfmod does not support transparent ioctls */
449 sioc.ic_cmd = PFIOCSETF;
450 sioc.ic_timout = 5;
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);
8bc98059 456 }
d62a17ae 457
458 circuit->fd = fd;
459
460 return ISIS_OK;
8bc98059
PJ
461}
462
463/*
464 * Create the socket and set the tx/rx funcs
465 */
d62a17ae 466int isis_sock_init(struct isis_circuit *circuit)
8bc98059 467{
d62a17ae 468 int retval = ISIS_OK;
469
0cf6db21 470 frr_with_privs(&isisd_privs) {
d62a17ae 471
01b9e3fd 472 retval = open_dlpi_dev(circuit);
d62a17ae 473
01b9e3fd 474 if (retval != ISIS_OK) {
633fc9b1
DL
475 zlog_warn("%s: could not initialize the socket",
476 __func__);
01b9e3fd
DL
477 break;
478 }
d62a17ae 479
01b9e3fd
DL
480 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
481 circuit->tx = isis_send_pdu_bcast;
482 circuit->rx = isis_recv_pdu_bcast;
483 } else {
484 zlog_warn("isis_sock_init(): unknown circuit type");
485 retval = ISIS_WARNING;
486 break;
487 }
633fc9b1 488 }
8bc98059 489
d62a17ae 490 return retval;
8bc98059
PJ
491}
492
d7c0a89a 493int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa)
8bc98059 494{
d62a17ae 495 struct pollfd fds[1];
496 struct strbuf ctlbuf, databuf;
497 int flags, retv;
498 dl_unitdata_ind_t *dui = (dl_unitdata_ind_t *)dlpi_ctl;
499
500 memset(fds, 0, sizeof(fds));
501 fds[0].fd = circuit->fd;
502 fds[0].events = POLLIN | POLLPRI;
503 if (poll(fds, 1, 0) <= 0)
504 return ISIS_WARNING;
505
506 memset(&ctlbuf, 0, sizeof(ctlbuf));
507 memset(&databuf, 0, sizeof(databuf));
508 ctlbuf.maxlen = sizeof(dlpi_ctl);
509 ctlbuf.buf = (void *)dlpi_ctl;
510 databuf.maxlen = sizeof(sock_buff);
511 databuf.buf = (void *)sock_buff;
512 flags = 0;
513 retv = getmsg(circuit->fd, &ctlbuf, &databuf, &flags);
514
515 if (retv < 0) {
516 zlog_warn("isis_recv_pdu_bcast: getmsg failed: %s",
517 safe_strerror(errno));
518 return ISIS_WARNING;
519 }
520
521 if (retv & (MORECTL | MOREDATA)) {
522 while (retv & (MORECTL | MOREDATA)) {
523 flags = 0;
524 retv = getmsg(circuit->fd, &ctlbuf, &databuf, &flags);
525 }
526 return ISIS_WARNING;
8bc98059 527 }
8bc98059 528
d62a17ae 529 if (ctlbuf.len < (ssize_t)DL_UNITDATA_IND_SIZE
530 || dui->dl_primitive != DL_UNITDATA_IND)
531 return ISIS_WARNING;
8bc98059 532
d62a17ae 533 if (dui->dl_src_addr_length != ETHERADDRL + 2
534 || dui->dl_src_addr_offset < DL_UNITDATA_IND_SIZE
535 || dui->dl_src_addr_offset + dui->dl_src_addr_length
536 > (size_t)ctlbuf.len)
537 return ISIS_WARNING;
8bc98059 538
d62a17ae 539 memcpy(ssnpa,
540 (char *)dui + dui->dl_src_addr_offset
541 + (circuit->sap_length > 0 ? circuit->sap_length : 0),
542 ETHERADDRL);
8bc98059 543
d62a17ae 544 if (databuf.len < LLC_LEN || sock_buff[0] != ISO_SAP
545 || sock_buff[1] != ISO_SAP || sock_buff[2] != 3)
546 return ISIS_WARNING;
8bc98059 547
d62a17ae 548 stream_write(circuit->rcv_stream, sock_buff + LLC_LEN,
549 databuf.len - LLC_LEN);
550 stream_set_getp(circuit->rcv_stream, 0);
8bc98059 551
d62a17ae 552 return ISIS_OK;
8bc98059
PJ
553}
554
d62a17ae 555int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
8bc98059 556{
d62a17ae 557 dl_unitdata_req_t *dur = (dl_unitdata_req_t *)dlpi_ctl;
558 char *dstaddr;
d7c0a89a 559 unsigned short *dstsap;
d62a17ae 560 int buflen;
561 int rv;
562
563 buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN;
564 if ((size_t)buflen > sizeof(sock_buff)) {
565 zlog_warn(
566 "isis_send_pdu_bcast: sock_buff size %zu is less than "
567 "output pdu size %d on circuit %s",
568 sizeof(sock_buff), buflen, circuit->interface->name);
569 return ISIS_WARNING;
570 }
571
572 stream_set_getp(circuit->snd_stream, 0);
573
574 memset(dur, 0, sizeof(*dur));
575 dur->dl_primitive = DL_UNITDATA_REQ;
576 dur->dl_dest_addr_length = ETHERADDRL + 2;
577 dur->dl_dest_addr_offset = sizeof(*dur);
578
579 dstaddr = (char *)(dur + 1);
580 if (circuit->sap_length < 0) {
d7c0a89a 581 dstsap = (unsigned short *)(dstaddr + ETHERADDRL);
d62a17ae 582 } else {
d7c0a89a 583 dstsap = (unsigned short *)dstaddr;
d62a17ae 584 dstaddr += circuit->sap_length;
585 }
586 if (level == 1)
587 memcpy(dstaddr, ALL_L1_ISS, ETHERADDRL);
588 else
589 memcpy(dstaddr, ALL_L2_ISS, ETHERADDRL);
590 /* Note: DLPI SAP values are in host byte order */
591 *dstsap = buflen;
592
593 sock_buff[0] = ISO_SAP;
594 sock_buff[1] = ISO_SAP;
595 sock_buff[2] = 0x03;
596 memcpy(sock_buff + LLC_LEN, circuit->snd_stream->data,
597 stream_get_endp(circuit->snd_stream));
598 rv = dlpisend(circuit->fd, dur, sizeof(*dur) + dur->dl_dest_addr_length,
599 sock_buff, buflen, 0);
600 if (rv < 0) {
601 zlog_warn("IS-IS dlpi: could not transmit packet on %s: %s",
602 circuit->interface->name, safe_strerror(errno));
603 if (ERRNO_IO_RETRY(errno))
604 return ISIS_WARNING;
605 return ISIS_ERROR;
606 }
607
608 return ISIS_OK;
8bc98059 609}
745bf05f
DL
610
611#endif /* ISIS_METHOD == ISIS_METHOD_DLPI */