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