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