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