]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_dlpi.c
isisd: Remove #if 0 code
[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
2b64873d
DL
57static const uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14};
58static const uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15};
59static const uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05};
207a1429 60static uint8_t sock_buff[16384];
8bc98059 61
d7c0a89a 62static unsigned short pf_filter[] = {
d62a17ae 63 ENF_PUSHWORD + 0, /* Get the SSAP/DSAP values */
64 ENF_PUSHLIT | ENF_CAND, /* Check them */
65 ISO_SAP | (ISO_SAP << 8),
66 ENF_PUSHWORD + 1, /* Get the control value */
67 ENF_PUSHLIT | ENF_AND, /* Isolate it */
8bc98059 68#ifdef _BIG_ENDIAN
d62a17ae 69 0xFF00,
8bc98059 70#else
d62a17ae 71 0x00FF,
8bc98059 72#endif
d62a17ae 73 ENF_PUSHLIT | ENF_CAND, /* Test for expected value */
8bc98059 74#ifdef _BIG_ENDIAN
d62a17ae 75 0x0300
8bc98059 76#else
d62a17ae 77 0x0003
8bc98059
PJ
78#endif
79};
80
81/*
82 * We would like to use something like libdlpi here, but that's not present on
83 * all versions of Solaris or on any non-Solaris system, so it's nowhere near
84 * as portable as we'd like. Thus, we use the standards-conformant DLPI
85 * interfaces plus the (optional; not needed) Solaris packet filter module.
86 */
87
d62a17ae 88static int dlpisend(int fd, const void *cbuf, size_t cbuflen, const void *dbuf,
89 size_t dbuflen, int flags)
8bc98059 90{
d62a17ae 91 const struct strbuf *ctlptr = NULL;
92 const struct strbuf *dataptr = NULL;
93 struct strbuf ctlbuf, databuf;
94 int rv;
95
96 if (cbuf != NULL) {
97 memset(&ctlbuf, 0, sizeof(ctlbuf));
98 ctlbuf.len = cbuflen;
99 ctlbuf.buf = (void *)cbuf;
100 ctlptr = &ctlbuf;
101 }
102
103 if (dbuf != NULL) {
104 memset(&databuf, 0, sizeof(databuf));
105 databuf.len = dbuflen;
106 databuf.buf = (void *)dbuf;
107 dataptr = &databuf;
108 }
109
110 /* We assume this doesn't happen often and isn't operationally
111 * significant */
112 rv = putmsg(fd, ctlptr, dataptr, flags);
113 if (rv == -1 && dbuf == NULL) {
114 /*
115 * For actual PDU transmission - recognizable buf dbuf != NULL,
116 * the error is passed upwards and should not be printed here.
117 */
118 zlog_debug("%s: putmsg: %s", __func__, safe_strerror(errno));
119 }
120 return rv;
8bc98059
PJ
121}
122
d62a17ae 123static ssize_t dlpirctl(int fd)
8bc98059 124{
d62a17ae 125 struct pollfd fds[1];
126 struct strbuf ctlbuf, databuf;
127 int flags, retv;
128
129 do {
130 /* Poll is used here in case the device doesn't speak DLPI
131 * correctly */
132 memset(fds, 0, sizeof(fds));
133 fds[0].fd = fd;
134 fds[0].events = POLLIN | POLLPRI;
135 if (poll(fds, 1, 1000) <= 0)
136 return -1;
137
138 memset(&ctlbuf, 0, sizeof(ctlbuf));
139 memset(&databuf, 0, sizeof(databuf));
140 ctlbuf.maxlen = sizeof(dlpi_ctl);
141 ctlbuf.buf = (void *)dlpi_ctl;
142 databuf.maxlen = sizeof(sock_buff);
143 databuf.buf = (void *)sock_buff;
144 flags = 0;
145 retv = getmsg(fd, &ctlbuf, &databuf, &flags);
146
147 if (retv < 0)
148 return -1;
149 } while (ctlbuf.len == 0);
150
151 if (!(retv & MORECTL)) {
152 while (retv & MOREDATA) {
153 flags = 0;
154 retv = getmsg(fd, NULL, &databuf, &flags);
155 }
156 return ctlbuf.len;
157 }
8bc98059 158
d62a17ae 159 while (retv & MORECTL) {
160 flags = 0;
161 retv = getmsg(fd, &ctlbuf, &databuf, &flags);
8bc98059 162 }
d62a17ae 163 return -1;
8bc98059
PJ
164}
165
d62a17ae 166static int dlpiok(int fd, t_uscalar_t oprim)
8bc98059 167{
d62a17ae 168 int retv;
169 dl_ok_ack_t *doa = (dl_ok_ack_t *)dlpi_ctl;
170
171 retv = dlpirctl(fd);
172 if (retv < (ssize_t)DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK
173 || doa->dl_correct_primitive != oprim) {
174 return -1;
175 } else {
176 return 0;
177 }
8bc98059
PJ
178}
179
d62a17ae 180static int dlpiinfo(int fd)
8bc98059 181{
d62a17ae 182 dl_info_req_t dir;
183 ssize_t retv;
184
185 memset(&dir, 0, sizeof(dir));
186 dir.dl_primitive = DL_INFO_REQ;
187 /* Info_req uses M_PCPROTO. */
188 dlpisend(fd, &dir, sizeof(dir), NULL, 0, RS_HIPRI);
189 retv = dlpirctl(fd);
190 if (retv < (ssize_t)DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK)
191 return -1;
192 else
193 return retv;
8bc98059
PJ
194}
195
d62a17ae 196static int dlpiopen(const char *devpath, ssize_t *acklen)
8bc98059 197{
d62a17ae 198 int fd, flags;
199
200 fd = open(devpath, O_RDWR | O_NONBLOCK | O_NOCTTY);
201 if (fd == -1)
202 return -1;
203
204 /* All that we want is for the open itself to be non-blocking, not I/O.
205 */
206 flags = fcntl(fd, F_GETFL, 0);
207 if (flags != -1)
208 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
209
210 /* After opening, ask for information */
211 if ((*acklen = dlpiinfo(fd)) == -1) {
212 close(fd);
213 return -1;
214 }
8bc98059 215
d62a17ae 216 return fd;
8bc98059
PJ
217}
218
d62a17ae 219static int dlpiattach(int fd, int unit)
8bc98059 220{
d62a17ae 221 dl_attach_req_t dar;
8bc98059 222
d62a17ae 223 memset(&dar, 0, sizeof(dar));
224 dar.dl_primitive = DL_ATTACH_REQ;
225 dar.dl_ppa = unit;
226 dlpisend(fd, &dar, sizeof(dar), NULL, 0, 0);
227 return dlpiok(fd, dar.dl_primitive);
8bc98059
PJ
228}
229
d62a17ae 230static int dlpibind(int fd)
8bc98059 231{
d62a17ae 232 dl_bind_req_t dbr;
233 int retv;
234 dl_bind_ack_t *dba = (dl_bind_ack_t *)dlpi_ctl;
235
236 memset(&dbr, 0, sizeof(dbr));
237 dbr.dl_primitive = DL_BIND_REQ;
238 dbr.dl_service_mode = DL_CLDLS;
239 dlpisend(fd, &dbr, sizeof(dbr), NULL, 0, 0);
240
241 retv = dlpirctl(fd);
242 if (retv < (ssize_t)DL_BIND_ACK_SIZE
243 || dba->dl_primitive != DL_BIND_ACK)
244 return -1;
245 else
246 return 0;
8bc98059
PJ
247}
248
d7c0a89a 249static int dlpimcast(int fd, const uint8_t *mcaddr)
8bc98059 250{
d62a17ae 251 struct {
252 dl_enabmulti_req_t der;
d7c0a89a 253 uint8_t addr[ETHERADDRL];
d62a17ae 254 } dler;
255
256 memset(&dler, 0, sizeof(dler));
257 dler.der.dl_primitive = DL_ENABMULTI_REQ;
258 dler.der.dl_addr_length = sizeof(dler.addr);
d7c0a89a 259 dler.der.dl_addr_offset = dler.addr - (uint8_t *)&dler;
d62a17ae 260 memcpy(dler.addr, mcaddr, sizeof(dler.addr));
261 dlpisend(fd, &dler, sizeof(dler), NULL, 0, 0);
262 return dlpiok(fd, dler.der.dl_primitive);
8bc98059
PJ
263}
264
d7c0a89a 265static int dlpiaddr(int fd, uint8_t *addr)
8bc98059 266{
d62a17ae 267 dl_phys_addr_req_t dpar;
268 dl_phys_addr_ack_t *dpaa = (dl_phys_addr_ack_t *)dlpi_ctl;
269 int retv;
270
271 memset(&dpar, 0, sizeof(dpar));
272 dpar.dl_primitive = DL_PHYS_ADDR_REQ;
273 dpar.dl_addr_type = DL_CURR_PHYS_ADDR;
274 dlpisend(fd, &dpar, sizeof(dpar), NULL, 0, 0);
275
276 retv = dlpirctl(fd);
277 if (retv < (ssize_t)DL_PHYS_ADDR_ACK_SIZE
278 || dpaa->dl_primitive != DL_PHYS_ADDR_ACK)
279 return -1;
280
281 if (dpaa->dl_addr_offset < DL_PHYS_ADDR_ACK_SIZE
282 || dpaa->dl_addr_length != ETHERADDRL
283 || dpaa->dl_addr_offset + dpaa->dl_addr_length > (size_t)retv)
284 return -1;
285
286 bcopy((char *)dpaa + dpaa->dl_addr_offset, addr, ETHERADDRL);
287 return 0;
8bc98059
PJ
288}
289
d62a17ae 290static int open_dlpi_dev(struct isis_circuit *circuit)
8bc98059 291{
d62a17ae 292 int fd = -1, unit, retval;
293 char devpath[MAXPATHLEN];
294 dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl;
295 ssize_t acklen;
296
297 /* Only broadcast-type are supported at the moment */
298 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
299 zlog_warn("%s: non-broadcast interface %s", __func__,
300 circuit->interface->name);
301 return ISIS_WARNING;
302 }
303
304 /* Try the vanity node first, if permitted */
305 if (getenv("DLPI_DEVONLY") == NULL) {
306 (void)snprintf(devpath, sizeof(devpath), "/dev/net/%s",
307 circuit->interface->name);
308 fd = dlpiopen(devpath, &acklen);
309 }
310
311 /* Now try as an ordinary Style 1 node */
312 if (fd == -1) {
313 (void)snprintf(devpath, sizeof(devpath), "/dev/%s",
314 circuit->interface->name);
315 unit = -1;
316 fd = dlpiopen(devpath, &acklen);
317 }
318
319 /* If that fails, try again as Style 2 */
320 if (fd == -1) {
321 char *cp;
322
323 cp = devpath + strlen(devpath);
324 while (--cp >= devpath && isdigit(*cp))
325 ;
326 unit = strtol(cp, NULL, 0);
327 *cp = '\0';
328 fd = dlpiopen(devpath, &acklen);
329
330 /* If that too fails, then the device really doesn't exist */
331 if (fd == -1) {
332 zlog_warn("%s: unknown interface %s", __func__,
333 circuit->interface->name);
334 return ISIS_WARNING;
335 }
336
337 /* Double check the DLPI style */
338 if (dia->dl_provider_style != DL_STYLE2) {
339 zlog_warn(
340 "open_dlpi_dev(): interface %s: %s is not style 2",
341 circuit->interface->name, devpath);
342 close(fd);
343 return ISIS_WARNING;
344 }
345
346 /* If it succeeds, then we need to attach to the unit specified
347 */
348 dlpiattach(fd, unit);
349
350 /* Reget the information, as it may be different per node */
351 if ((acklen = dlpiinfo(fd)) == -1) {
352 close(fd);
353 return ISIS_WARNING;
354 }
355 } else {
356 /* Double check the DLPI style */
357 if (dia->dl_provider_style != DL_STYLE1) {
358 zlog_warn(
359 "open_dlpi_dev(): interface %s: %s is not style 1",
360 circuit->interface->name, devpath);
361 close(fd);
362 return ISIS_WARNING;
363 }
8bc98059
PJ
364 }
365
d62a17ae 366 /* Check that the interface we've got is the kind we expect */
367 if ((dia->dl_sap_length != 2 && dia->dl_sap_length != -2)
368 || dia->dl_service_mode != DL_CLDLS
369 || dia->dl_addr_length != ETHERADDRL + 2
370 || dia->dl_brdcst_addr_length != ETHERADDRL) {
371 zlog_warn("%s: unsupported interface type for %s", __func__,
372 circuit->interface->name);
373 close(fd);
374 return ISIS_WARNING;
375 }
376 switch (dia->dl_mac_type) {
377 case DL_CSMACD:
378 case DL_ETHER:
379 case DL_100VG:
380 case DL_100VGTPR:
381 case DL_ETH_CSMA:
382 case DL_100BT:
383 break;
384 default:
385 zlog_warn("%s: unexpected mac type on %s: %lld", __func__,
386 circuit->interface->name,
387 (long long)dia->dl_mac_type);
388 close(fd);
389 return ISIS_WARNING;
390 }
391
392 circuit->sap_length = dia->dl_sap_length;
393
394 /*
395 * The local hardware address is something that should be provided by
396 * way of
397 * sockaddr_dl for the interface, but isn't on Solaris. We set it here
398 * based
399 * on DLPI's reported address to avoid roto-tilling the world.
400 * (Note that isis_circuit_if_add on Solaris doesn't set the snpa.)
401 *
402 * Unfortunately, GLD is broken and doesn't provide the address after
403 * attach,
404 * so we need to be careful and use DL_PHYS_ADDR_REQ instead.
405 */
406 if (dlpiaddr(fd, circuit->u.bc.snpa) == -1) {
407 zlog_warn(
408 "open_dlpi_dev(): interface %s: unable to get MAC address",
409 circuit->interface->name);
410 close(fd);
411 return ISIS_WARNING;
8bc98059
PJ
412 }
413
d62a17ae 414 /* Now bind to SAP 0. This gives us 802-type traffic. */
415 if (dlpibind(fd) == -1) {
416 zlog_warn("%s: cannot bind SAP 0 on %s", __func__,
417 circuit->interface->name);
418 close(fd);
419 return ISIS_WARNING;
420 }
8bc98059 421
d62a17ae 422 /*
423 * Join to multicast groups according to
424 * 8.4.2 - Broadcast subnetwork IIH PDUs
425 */
426 retval = 0;
427 retval |= dlpimcast(fd, ALL_L1_ISS);
428 retval |= dlpimcast(fd, ALL_ISS);
429 retval |= dlpimcast(fd, ALL_L2_ISS);
430
431 if (retval != 0) {
432 zlog_warn("%s: unable to join multicast on %s", __func__,
433 circuit->interface->name);
434 close(fd);
435 return ISIS_WARNING;
8bc98059 436 }
d62a17ae 437
438 /* Push on the packet filter to avoid stray 802 packets */
439 if (ioctl(fd, I_PUSH, "pfmod") == 0) {
440 struct packetfilt pfil;
441 struct strioctl sioc;
442
443 pfil.Pf_Priority = 0;
97b5d752 444 pfil.Pf_FilterLen = array_size(pf_filter);
d62a17ae 445 memcpy(pfil.Pf_Filter, pf_filter, sizeof(pf_filter));
446 /* pfmod does not support transparent ioctls */
447 sioc.ic_cmd = PFIOCSETF;
448 sioc.ic_timout = 5;
449 sioc.ic_len = sizeof(struct packetfilt);
450 sioc.ic_dp = (char *)&pfil;
451 if (ioctl(fd, I_STR, &sioc) == -1)
452 zlog_warn("%s: could not perform PF_IOCSETF on %s",
453 __func__, circuit->interface->name);
8bc98059 454 }
d62a17ae 455
456 circuit->fd = fd;
457
458 return ISIS_OK;
8bc98059
PJ
459}
460
461/*
462 * Create the socket and set the tx/rx funcs
463 */
d62a17ae 464int isis_sock_init(struct isis_circuit *circuit)
8bc98059 465{
d62a17ae 466 int retval = ISIS_OK;
467
0cf6db21 468 frr_with_privs(&isisd_privs) {
d62a17ae 469
01b9e3fd 470 retval = open_dlpi_dev(circuit);
d62a17ae 471
01b9e3fd 472 if (retval != ISIS_OK) {
633fc9b1
DL
473 zlog_warn("%s: could not initialize the socket",
474 __func__);
01b9e3fd
DL
475 break;
476 }
d62a17ae 477
01b9e3fd
DL
478 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
479 circuit->tx = isis_send_pdu_bcast;
480 circuit->rx = isis_recv_pdu_bcast;
481 } else {
482 zlog_warn("isis_sock_init(): unknown circuit type");
483 retval = ISIS_WARNING;
484 break;
485 }
633fc9b1 486 }
8bc98059 487
d62a17ae 488 return retval;
8bc98059
PJ
489}
490
d7c0a89a 491int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa)
8bc98059 492{
d62a17ae 493 struct pollfd fds[1];
494 struct strbuf ctlbuf, databuf;
495 int flags, retv;
496 dl_unitdata_ind_t *dui = (dl_unitdata_ind_t *)dlpi_ctl;
497
498 memset(fds, 0, sizeof(fds));
499 fds[0].fd = circuit->fd;
500 fds[0].events = POLLIN | POLLPRI;
501 if (poll(fds, 1, 0) <= 0)
502 return ISIS_WARNING;
503
504 memset(&ctlbuf, 0, sizeof(ctlbuf));
505 memset(&databuf, 0, sizeof(databuf));
506 ctlbuf.maxlen = sizeof(dlpi_ctl);
507 ctlbuf.buf = (void *)dlpi_ctl;
508 databuf.maxlen = sizeof(sock_buff);
509 databuf.buf = (void *)sock_buff;
510 flags = 0;
511 retv = getmsg(circuit->fd, &ctlbuf, &databuf, &flags);
512
513 if (retv < 0) {
514 zlog_warn("isis_recv_pdu_bcast: getmsg failed: %s",
515 safe_strerror(errno));
516 return ISIS_WARNING;
517 }
518
519 if (retv & (MORECTL | MOREDATA)) {
520 while (retv & (MORECTL | MOREDATA)) {
521 flags = 0;
522 retv = getmsg(circuit->fd, &ctlbuf, &databuf, &flags);
523 }
524 return ISIS_WARNING;
8bc98059 525 }
8bc98059 526
d62a17ae 527 if (ctlbuf.len < (ssize_t)DL_UNITDATA_IND_SIZE
528 || dui->dl_primitive != DL_UNITDATA_IND)
529 return ISIS_WARNING;
8bc98059 530
d62a17ae 531 if (dui->dl_src_addr_length != ETHERADDRL + 2
532 || dui->dl_src_addr_offset < DL_UNITDATA_IND_SIZE
533 || dui->dl_src_addr_offset + dui->dl_src_addr_length
534 > (size_t)ctlbuf.len)
535 return ISIS_WARNING;
8bc98059 536
d62a17ae 537 memcpy(ssnpa,
538 (char *)dui + dui->dl_src_addr_offset
539 + (circuit->sap_length > 0 ? circuit->sap_length : 0),
540 ETHERADDRL);
8bc98059 541
d62a17ae 542 if (databuf.len < LLC_LEN || sock_buff[0] != ISO_SAP
543 || sock_buff[1] != ISO_SAP || sock_buff[2] != 3)
544 return ISIS_WARNING;
8bc98059 545
d62a17ae 546 stream_write(circuit->rcv_stream, sock_buff + LLC_LEN,
547 databuf.len - LLC_LEN);
548 stream_set_getp(circuit->rcv_stream, 0);
8bc98059 549
d62a17ae 550 return ISIS_OK;
8bc98059
PJ
551}
552
d62a17ae 553int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
8bc98059 554{
d62a17ae 555 dl_unitdata_req_t *dur = (dl_unitdata_req_t *)dlpi_ctl;
556 char *dstaddr;
d7c0a89a 557 unsigned short *dstsap;
d62a17ae 558 int buflen;
559 int rv;
560
561 buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN;
562 if ((size_t)buflen > sizeof(sock_buff)) {
563 zlog_warn(
3efd0893 564 "isis_send_pdu_bcast: sock_buff size %zu is less than output pdu size %d on circuit %s",
d62a17ae 565 sizeof(sock_buff), buflen, circuit->interface->name);
566 return ISIS_WARNING;
567 }
568
569 stream_set_getp(circuit->snd_stream, 0);
570
571 memset(dur, 0, sizeof(*dur));
572 dur->dl_primitive = DL_UNITDATA_REQ;
573 dur->dl_dest_addr_length = ETHERADDRL + 2;
574 dur->dl_dest_addr_offset = sizeof(*dur);
575
576 dstaddr = (char *)(dur + 1);
577 if (circuit->sap_length < 0) {
d7c0a89a 578 dstsap = (unsigned short *)(dstaddr + ETHERADDRL);
d62a17ae 579 } else {
d7c0a89a 580 dstsap = (unsigned short *)dstaddr;
d62a17ae 581 dstaddr += circuit->sap_length;
582 }
583 if (level == 1)
584 memcpy(dstaddr, ALL_L1_ISS, ETHERADDRL);
585 else
586 memcpy(dstaddr, ALL_L2_ISS, ETHERADDRL);
587 /* Note: DLPI SAP values are in host byte order */
588 *dstsap = buflen;
589
590 sock_buff[0] = ISO_SAP;
591 sock_buff[1] = ISO_SAP;
592 sock_buff[2] = 0x03;
593 memcpy(sock_buff + LLC_LEN, circuit->snd_stream->data,
594 stream_get_endp(circuit->snd_stream));
595 rv = dlpisend(circuit->fd, dur, sizeof(*dur) + dur->dl_dest_addr_length,
596 sock_buff, buflen, 0);
597 if (rv < 0) {
598 zlog_warn("IS-IS dlpi: could not transmit packet on %s: %s",
599 circuit->interface->name, safe_strerror(errno));
600 if (ERRNO_IO_RETRY(errno))
601 return ISIS_WARNING;
602 return ISIS_ERROR;
603 }
604
605 return ISIS_OK;
8bc98059 606}
745bf05f
DL
607
608#endif /* ISIS_METHOD == ISIS_METHOD_DLPI */