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