blob: 4f9af61ef57886800cf5c07c176ff59d7455174d [file] [log] [blame]
jardineb5d44e2003-12-23 08:09:43 +00001/*
2 * IS-IS Rout(e)ing protocol - isis_network.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 <sys/types.h>
24#include <sys/socket.h>
25#include <errno.h>
26#include <zebra.h>
hasso37da8c02004-05-19 11:38:40 +000027#ifdef GNU_LINUX
jardineb5d44e2003-12-23 08:09:43 +000028#include <net/ethernet.h> /* the L2 protocols */
hasso37da8c02004-05-19 11:38:40 +000029#else
30#include <net/if.h>
31#include <netinet/if_ether.h>
32#endif
33
jardineb5d44e2003-12-23 08:09:43 +000034
35#include "log.h"
36#include "stream.h"
37#include "if.h"
38
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_constants.h"
48#include "isisd/isis_circuit.h"
49#include "isisd/isis_network.h"
50
jardin9e867fe2003-12-23 08:56:18 +000051#include "privs.h"
52
53extern struct zebra_privs_t isisd_privs;
54
jardineb5d44e2003-12-23 08:09:43 +000055/*
56 * On linux we can use the packet(7) sockets, in other OSs we have to do with
57 * Berkley Packet Filter (BPF). Please tell me if you can think of a better
58 * way...
59 */
60#ifdef GNU_LINUX
61#include <netpacket/packet.h>
62#else
63#include <sys/time.h>
64#include <sys/ioctl.h>
65#include <net/bpf.h>
66struct bpf_insn llcfilter[] = {
67 BPF_STMT(BPF_LD+BPF_B+BPF_ABS, ETHER_HDR_LEN), /* check first byte */
68 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ISO_SAP, 0, 5),
69 BPF_STMT(BPF_LD+BPF_B+BPF_ABS, ETHER_HDR_LEN+1),
70 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ISO_SAP, 0, 3), /* check second byte */
71 BPF_STMT(BPF_LD+BPF_B+BPF_ABS, ETHER_HDR_LEN+2),
72 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x03, 0, 1), /* check third byte */
73 BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
74 BPF_STMT(BPF_RET+BPF_K, 0)
75};
76int readblen = 0;
77u_char *readbuff = NULL;
78#endif /* GNU_LINUX */
79
80/*
81 * Table 9 - Architectural constans for use with ISO 8802 subnetworks
82 * ISO 10589 - 8.4.8
83 */
84
85u_char ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14};
86u_char ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15};
87u_char ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05};
88u_char ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04};
89
90#ifdef GNU_LINUX
91static char discard_buff[8192];
92#endif
93static char sock_buff[8192];
94
95/*
96 * if level is 0 we are joining p2p multicast
97 * FIXME: and the p2p multicast being ???
98 */
99#ifdef GNU_LINUX
100int
101isis_multicast_join (int fd, int registerto, int if_num)
102{
103 struct packet_mreq mreq;
104
105 memset(&mreq, 0, sizeof(mreq));
106 mreq.mr_ifindex = if_num;
107 if (registerto) {
108 mreq.mr_type = PACKET_MR_MULTICAST;
109 mreq.mr_alen = ETH_ALEN;
110 if (registerto == 1)
111 memcpy (&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
112 else if (registerto == 2)
113 memcpy (&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
114 else if (registerto == 3)
115 memcpy (&mreq.mr_address, ALL_ISS, ETH_ALEN);
116 else
117 memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN);
118
119 } else {
120 mreq.mr_type = PACKET_MR_ALLMULTI;
121 }
122#ifdef EXTREME_DEBUG
123 zlog_info ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
124 "address = %02x:%02x:%02x:%02x:%02x:%02x",
125 fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1],
126 mreq.mr_address[2], mreq.mr_address[3],mreq.mr_address[4],
127 mreq.mr_address[5]);
128#endif /* EXTREME_DEBUG */
129 if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
130 sizeof (struct packet_mreq))) {
131 zlog_warn ("isis_multicast_join(): setsockopt(): %s", strerror (errno));
132 return ISIS_WARNING;
133 }
134
135 return ISIS_OK;
136}
137
138int
139open_packet_socket (struct isis_circuit *circuit)
140{
141 struct sockaddr_ll s_addr;
142 int fd, retval = ISIS_OK;
143
144 fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
145 if (fd < 0) {
146 zlog_warn ("open_packet_socket(): socket() failed %s", strerror (errno));
147 return ISIS_WARNING;
148 }
149
150 /*
151 * Bind to the physical interface
152 */
153 memset(&s_addr, 0, sizeof (struct sockaddr_ll));
154 s_addr.sll_family = AF_PACKET;
155 s_addr.sll_protocol = htons (ETH_P_ALL);
156 s_addr.sll_ifindex = circuit->interface->ifindex;
157
158 if (bind (fd, (struct sockaddr*) (&s_addr),
159 sizeof(struct sockaddr_ll)) < 0) {
160 zlog_warn ("open_packet_socket(): bind() failed: %s", strerror(errno));
161 return ISIS_WARNING;
162 }
163
164 circuit->fd = fd;
165
166 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
167 /*
168 * Join to multicast groups
169 * according to
170 * 8.4.2 - Broadcast subnetwork IIH PDUs
171 * FIXME: is there a case only one will fail??
172 */
173 if (circuit->circuit_is_type & IS_LEVEL_1) {
174 /* joining ALL_L1_ISS */
175 retval = isis_multicast_join (circuit->fd, 1,
176 circuit->interface->ifindex);
177 /* joining ALL_ISS */
178 retval = isis_multicast_join (circuit->fd, 3,
179 circuit->interface->ifindex);
180 }
181 if (circuit->circuit_is_type & IS_LEVEL_2)
182 /* joining ALL_L2_ISS */
183 retval = isis_multicast_join (circuit->fd, 2,
184 circuit->interface->ifindex);
185 } else {
186 retval = isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex);
187 }
188
189 return retval;
190}
191
192#else
193
194int
195open_bpf_dev (struct isis_circuit *circuit)
196{
197 int i = 0, fd;
198 char bpfdev[128];
199 struct ifreq ifr;
200 u_int16_t blen;
201 int true = 1, false = 0;
202 struct timeval timeout;
203 struct bpf_program bpf_prog;
204
205 do {
206 (void)snprintf(bpfdev, sizeof(bpfdev), "/dev/bpf%d", i++);
207 fd = open(bpfdev, O_RDWR);
208 } while (fd < 0 && errno == EBUSY);
209
210 if (fd < 0) {
211 zlog_warn ("open_bpf_dev(): failed to create bpf socket: %s",
212 strerror (errno));
213 return ISIS_WARNING;
214 }
215
216 zlog_info ("Opened BPF device %s", bpfdev);
217
218 memcpy (ifr.ifr_name, circuit->interface->name, sizeof(ifr.ifr_name));
219 if (ioctl (fd, BIOCSETIF, (caddr_t)&ifr) < 0 ) {
220 zlog_warn ("open_bpf_dev(): failed to bind to interface: %s",
221 strerror (errno));
222 return ISIS_WARNING;
223 }
224
225
226 if (ioctl (fd, BIOCGBLEN, (caddr_t)&blen) < 0) {
227 zlog_warn ("failed to get BPF buffer len");
228 blen = circuit->interface->mtu;
229 }
230
231 readblen = blen;
232
233 if (readbuff == NULL)
234 readbuff = malloc (blen);
235
236 zlog_info ("BPF buffer len = %u", blen);
237
238 /* BPF(4): reads return immediately upon packet reception.
239 * Otherwise, a read will block until either the kernel
240 * buffer becomes full or a timeout occurs.
241 */
242 if (ioctl (fd, BIOCIMMEDIATE, (caddr_t)&true) < 0) {
243 zlog_warn ("failed to set BPF dev to immediate mode");
244 }
245
hasso37da8c02004-05-19 11:38:40 +0000246#ifdef BIOCSSEESENT
jardineb5d44e2003-12-23 08:09:43 +0000247 /*
248 * We want to see only incoming packets
249 */
250 if (ioctl (fd, BIOCSSEESENT, (caddr_t)&false) < 0) {
251 zlog_warn ("failed to set BPF dev to incoming only mode");
252 }
hasso37da8c02004-05-19 11:38:40 +0000253#endif
jardineb5d44e2003-12-23 08:09:43 +0000254
255 /*
256 * ...but all of them
257 */
258 if (ioctl (fd, BIOCPROMISC, (caddr_t)&true) < 0) {
259 zlog_warn ("failed to set BPF dev to promiscuous mode");
260 }
261
262
263 /*
264 * If the buffer length is smaller than our mtu, lets try to increase it
265 */
266 if (blen < circuit->interface->mtu) {
267 if (ioctl (fd, BIOCSBLEN, &circuit->interface->mtu) < 0) {
268 zlog_warn ("failed to set BPF buffer len (%u to %u)", blen,
269 circuit->interface->mtu);
270 }
271 }
272
273 /*
274 * Set a timeout parameter - hope this helps select()
275 */
276 timeout.tv_sec = 600;
277 timeout.tv_usec = 0;
278 if (ioctl (fd, BIOCSRTIMEOUT, (caddr_t)&timeout) < 0) {
279 zlog_warn ("failed to set BPF device timeout");
280 }
281
282 /*
283 * And set the filter
284 */
285 memset (&bpf_prog, 0, sizeof (struct bpf_program));
286 bpf_prog.bf_len = 8;
287 bpf_prog.bf_insns = &(llcfilter[0]);
288 if (ioctl (fd, BIOCSETF, (caddr_t)&bpf_prog) < 0) {
289 zlog_warn ("open_bpf_dev(): failed to install filter: %s",
290 strerror (errno));
291 return ISIS_WARNING;
292 }
293
294
295 assert (fd > 0);
296
297 circuit->fd = fd;
298
299 return ISIS_OK;
300}
301
302#endif /* GNU_LINUX */
303
304/*
305 * Create the socket and set the tx/rx funcs
306 */
307int
308isis_sock_init (struct isis_circuit *circuit)
309{
310 int retval = ISIS_OK;
311
jardin9e867fe2003-12-23 08:56:18 +0000312 if ( isisd_privs.change (ZPRIVS_RAISE) )
313 zlog_err ("%s: could not raise privs, %s", __func__,
314 strerror (errno) );
jardineb5d44e2003-12-23 08:09:43 +0000315
316#ifdef GNU_LINUX
317 retval = open_packet_socket (circuit);
318#else
319 retval = open_bpf_dev (circuit);
320#endif
jardin9e867fe2003-12-23 08:56:18 +0000321
322 if (retval != ISIS_OK) {
323 zlog_warn("%s: could not initialize the socket",
324 __func__);
325 goto end;
jardineb5d44e2003-12-23 08:09:43 +0000326 }
jardin9e867fe2003-12-23 08:56:18 +0000327
328 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
329 circuit->tx = isis_send_pdu_bcast;
330 circuit->rx = isis_recv_pdu_bcast;
331 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
332 circuit->tx = isis_send_pdu_p2p;
333 circuit->rx = isis_recv_pdu_p2p;
334 } else {
335 zlog_warn ("isis_sock_init(): unknown circuit type");
336 retval = ISIS_WARNING;
337 goto end;
338 }
339
340end:
341 if ( isisd_privs.change (ZPRIVS_LOWER) )
342 zlog_err ("%s: could not lower privs, %s", __func__,
343 strerror (errno) );
344
jardineb5d44e2003-12-23 08:09:43 +0000345 return retval;
346}
347
348
349static inline int
350llc_check (u_char *llc)
351{
352
353 if(*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc +2) != 3)
354 return 0;
355
356 return 1;
357}
358
359#ifdef GNU_LINUX
360int
361isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char *ssnpa)
362{
363 int bytesread, addr_len;
364 struct sockaddr_ll s_addr;
365 u_char llc[LLC_LEN];
366
367 addr_len = sizeof (s_addr);
368
369 memset (&s_addr, 0, sizeof (struct sockaddr_ll));
370
371 bytesread = recvfrom (circuit->fd, (void *)&llc,
372 LLC_LEN, MSG_PEEK,
373 (struct sockaddr *)&s_addr, &addr_len);
374
375 if (bytesread < 0) {
376 zlog_warn ("isis_recv_packet_bcast(): fd %d, recvfrom (): %s",
377 circuit->fd, strerror (errno));
378 zlog_warn ("circuit is %s", circuit->interface->name);
379 zlog_warn ("circuit fd %d", circuit->fd);
380 zlog_warn ("bytesread %d", bytesread);
381 /* get rid of the packet */
382 bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
383 return ISIS_WARNING;
384 }
385 /*
386 * Filtering by llc field, discard packets sent by this host (other circuit)
387 */
388 if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING) {
389 /* Read the packet into discard buff */
390 bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
391 if (bytesread < 0)
392 zlog_warn ("isis_recv_pdu_bcast(): read() failed");
393 return ISIS_WARNING;
394 }
395
396 /* on lan we have to read to the static buff first */
397 bytesread = recvfrom (circuit->fd, sock_buff, circuit->interface->mtu, 0,
398 (struct sockaddr *)&s_addr, &addr_len);
399
400 /* then we lose the LLC */
401 memcpy (STREAM_DATA (circuit->rcv_stream),
402 sock_buff + LLC_LEN, bytesread - LLC_LEN);
403 circuit->rcv_stream->putp = bytesread - LLC_LEN;
404 circuit->rcv_stream->endp = bytesread - LLC_LEN;
405
406 memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
407
408 return ISIS_OK;
409}
410
411int
412isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char *ssnpa)
413{
414
415 int bytesread, addr_len;
416 struct sockaddr_ll s_addr;
417
418 memset (&s_addr, 0, sizeof (struct sockaddr_ll));
419 addr_len = sizeof (s_addr);
420
421 /* we can read directly to the stream */
422 bytesread = recvfrom (circuit->fd, STREAM_DATA (circuit->rcv_stream),
423 circuit->interface->mtu, 0,
424 (struct sockaddr *)&s_addr, &addr_len);
425
426 if(s_addr.sll_pkttype == PACKET_OUTGOING) {
427 /* Read the packet into discard buff */
428 bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff));
429 if (bytesread < 0)
430 zlog_warn ("isis_recv_pdu_p2p(): read() failed");
431 return ISIS_WARNING;
432 }
433
434 circuit->rcv_stream->putp = bytesread;
435 circuit->rcv_stream->endp = bytesread;
436
437 /* If we don't have protocol type 0x00FE which is
438 * ISO over GRE we exit with pain :)
439 */
440 if (ntohs(s_addr.sll_protocol) != 0x00FE) {
441 zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
442 ntohs(s_addr.sll_protocol));
443 return ISIS_WARNING;
444 }
445
446 memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
447
448 return ISIS_OK;
449}
450
451
452
453int
454isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
455{
456 /* we need to do the LLC in here because of P2P circuits, which will
457 * not need it
458 */
459 int written = 1;
460 struct sockaddr_ll sa;
461
462 stream_set_getp (circuit->snd_stream, 0);
463 memset (&sa, 0, sizeof (struct sockaddr_ll));
464 sa.sll_family = AF_PACKET;
465 sa.sll_protocol = htons (stream_get_endp(circuit->snd_stream)+LLC_LEN);
466 sa.sll_ifindex = circuit->interface->ifindex;
467 sa.sll_halen = ETH_ALEN;
468 if (level == 1)
469 memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
470 else
471 memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
472
473 /* on a broadcast circuit */
474 /* first we put the LLC in */
475 sock_buff[0] = 0xFE;
476 sock_buff[1] = 0xFE;
477 sock_buff[2] = 0x03;
478
479 /* then we copy the data */
480 memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data,
481 stream_get_endp (circuit->snd_stream));
482
483 /* now we can send this */
484 written = sendto (circuit->fd, sock_buff,
485 circuit->snd_stream->putp + LLC_LEN, 0,
486 (struct sockaddr *)&sa, sizeof (struct sockaddr_ll));
487
488
489 return ISIS_OK;
490}
491
492int
493isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
494{
495
496 int written = 1;
497 struct sockaddr_ll sa;
498
499 stream_set_getp (circuit->snd_stream, 0);
500 memset (&sa, 0, sizeof (struct sockaddr_ll));
501 sa.sll_family = AF_PACKET;
502 sa.sll_protocol = htons (stream_get_endp(circuit->snd_stream)+LLC_LEN);
503 sa.sll_ifindex = circuit->interface->ifindex;
504 sa.sll_halen = ETH_ALEN;
505 if (level == 1)
506 memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
507 else
508 memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
509
510
511 /* lets try correcting the protocol */
512 sa.sll_protocol = htons(0x00FE);
513 written = sendto (circuit->fd, circuit->snd_stream->data,
514 circuit->snd_stream->putp, 0, (struct sockaddr *)&sa,
515 sizeof (struct sockaddr_ll));
516
517 return ISIS_OK;
518}
519
520
521#else
522
523int
524isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char *ssnpa)
525{
526 int bytesread = 0, bytestoread, offset, one = 1;
527 struct bpf_hdr *bpf_hdr;
528
529 assert (circuit->fd > 0);
530
531 if (ioctl (circuit->fd, FIONREAD, (caddr_t)&bytestoread) < 0 ) {
532 zlog_warn ("ioctl() FIONREAD failed: %s", strerror (errno));
533 }
534
535 if (bytestoread) {
536 bytesread = read (circuit->fd, readbuff, readblen);
537 }
538 if (bytesread < 0) {
539 zlog_warn ("isis_recv_pdu_bcast(): read() failed: %s", strerror (errno));
540 return ISIS_WARNING;
541 }
542
543 if (bytesread == 0)
544 return ISIS_WARNING;
545
546 bpf_hdr = (struct bpf_hdr*)readbuff;
547
548 assert (bpf_hdr->bh_caplen == bpf_hdr->bh_datalen);
549
550 offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN;
551
552 /* then we lose the BPF, LLC and ethernet headers */
553 memcpy (STREAM_DATA (circuit->rcv_stream),
554 readbuff + offset,
555 bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN);
556
557 circuit->rcv_stream->putp = bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN;
558 circuit->rcv_stream->endp = bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN;
559 circuit->rcv_stream->getp = 0;
560
561 memcpy (ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN,
562 ETHER_ADDR_LEN);
563
564 if (ioctl (circuit->fd, BIOCFLUSH, &one) < 0)
565 zlog_warn ("Flushing failed: %s", strerror (errno));
566
567 return ISIS_OK;
568}
569
570int
571isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char *ssnpa)
572{
573 int bytesread;
574
575 bytesread = read (circuit->fd, STREAM_DATA(circuit->rcv_stream),
576 circuit->interface->mtu);
577
578 if (bytesread < 0) {
579 zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", strerror (errno));
580 return ISIS_WARNING;
581 }
582
583 circuit->rcv_stream->putp = bytesread;
584 circuit->rcv_stream->endp = bytesread;
585
586 return ISIS_OK;
587}
588
589
590
591int
592isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
593{
594 struct ether_header *eth;
595 int written;
596
597 stream_set_getp (circuit->snd_stream, 0);
598
599 /*
600 * First the eth header
601 */
602 eth = (struct ether_header *)sock_buff;
603 if (level == 1)
604 memcpy (eth->ether_dhost, ALL_L1_ISS, ETHER_ADDR_LEN);
605 else
606 memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN);
607 memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN);
608 eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
609
610 /*
611 * Then the LLC
612 */
613 sock_buff[ETHER_HDR_LEN] = ISO_SAP;
614 sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP;
615 sock_buff[ETHER_HDR_LEN + 2] = 0x03;
616
617 /* then we copy the data */
618 memcpy (sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
619 stream_get_endp (circuit->snd_stream));
620
621 /* now we can send this */
622 written = write (circuit->fd, sock_buff,
623 circuit->snd_stream->putp + LLC_LEN + ETHER_HDR_LEN);
624
625
626 return ISIS_OK;
627}
628
629int
630isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
631{
632
633
634 return ISIS_OK;
635}
636
637
638
639
640#endif /* GNU_LINUX */
641
642
643
644
645