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