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