Import Upstream version 1.2.2
[quagga-debian.git] / isisd / isis_pfpacket.c
1 /*
2  * IS-IS Rout(e)ing protocol - isis_pfpacket.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_PFPACKET
25 #include <net/ethernet.h>       /* the L2 protocols */
26 #include <netpacket/packet.h>
27
28 #include "log.h"
29 #include "network.h"
30 #include "stream.h"
31 #include "if.h"
32
33 #include "isisd/dict.h"
34 #include "isisd/include-netbsd/iso.h"
35 #include "isisd/isis_constants.h"
36 #include "isisd/isis_common.h"
37 #include "isisd/isis_circuit.h"
38 #include "isisd/isis_flags.h"
39 #include "isisd/isisd.h"
40 #include "isisd/isis_constants.h"
41 #include "isisd/isis_circuit.h"
42 #include "isisd/isis_network.h"
43
44 #include "privs.h"
45
46 extern struct zebra_privs_t isisd_privs;
47
48 /*
49  * Table 9 - Architectural constants for use with ISO 8802 subnetworks
50  * ISO 10589 - 8.4.8
51  */
52
53 u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
54 u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
55 u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
56 u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
57
58 static uint8_t discard_buff[8192];
59 static uint8_t sock_buff[8192];
60
61 /*
62  * if level is 0 we are joining p2p multicast
63  * FIXME: and the p2p multicast being ???
64  */
65 static int
66 isis_multicast_join (int fd, int registerto, int if_num)
67 {
68   struct packet_mreq mreq;
69
70   memset (&mreq, 0, sizeof (mreq));
71   mreq.mr_ifindex = if_num;
72   if (registerto)
73     {
74       mreq.mr_type = PACKET_MR_MULTICAST;
75       mreq.mr_alen = ETH_ALEN;
76       if (registerto == 1)
77         memcpy (&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
78       else if (registerto == 2)
79         memcpy (&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
80       else if (registerto == 3)
81         memcpy (&mreq.mr_address, ALL_ISS, ETH_ALEN);
82       else
83         memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN);
84
85     }
86   else
87     {
88       mreq.mr_type = PACKET_MR_ALLMULTI;
89     }
90 #ifdef EXTREME_DEBUG
91   zlog_debug ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
92               "address = %02x:%02x:%02x:%02x:%02x:%02x",
93               fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1],
94               mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4],
95               mreq.mr_address[5]);
96 #endif /* EXTREME_DEBUG */
97   if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
98                   sizeof (struct packet_mreq)))
99     {
100       zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno));
101       return ISIS_WARNING;
102     }
103
104   return ISIS_OK;
105 }
106
107 static int
108 open_packet_socket (struct isis_circuit *circuit)
109 {
110   struct sockaddr_ll s_addr;
111   int fd, retval = ISIS_OK;
112
113   fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
114   if (fd < 0)
115     {
116       zlog_warn ("open_packet_socket(): socket() failed %s",
117                  safe_strerror (errno));
118       return ISIS_WARNING;
119     }
120
121   /*
122    * Bind to the physical interface
123    */
124   memset (&s_addr, 0, sizeof (struct sockaddr_ll));
125   s_addr.sll_family = AF_PACKET;
126   s_addr.sll_protocol = htons (ETH_P_ALL);
127   s_addr.sll_ifindex = circuit->interface->ifindex;
128
129   if (bind (fd, (struct sockaddr *) (&s_addr),
130             sizeof (struct sockaddr_ll)) < 0)
131     {
132       zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno));
133       close (fd);
134       return ISIS_WARNING;
135     }
136
137   circuit->fd = fd;
138
139   if (if_is_broadcast (circuit->interface))
140     {
141       /*
142        * Join to multicast groups
143        * according to
144        * 8.4.2 - Broadcast subnetwork IIH PDUs
145        * FIXME: is there a case only one will fail??
146        */
147       /* joining ALL_L1_ISS */
148       retval |= isis_multicast_join (circuit->fd, 1,
149                                       circuit->interface->ifindex);
150       /* joining ALL_L2_ISS */
151       retval |= isis_multicast_join (circuit->fd, 2,
152                                       circuit->interface->ifindex);
153       /* joining ALL_ISS (used in RFC 5309 p2p-over-lan as well) */
154       retval |= isis_multicast_join (circuit->fd, 3,
155                                     circuit->interface->ifindex);
156     }
157   else
158     {
159       retval =
160         isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex);
161     }
162
163   return retval;
164 }
165
166 /*
167  * Create the socket and set the tx/rx funcs
168  */
169 int
170 isis_sock_init (struct isis_circuit *circuit)
171 {
172   int retval = ISIS_OK;
173
174   if (isisd_privs.change (ZPRIVS_RAISE))
175     zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
176
177   retval = open_packet_socket (circuit);
178
179   if (retval != ISIS_OK)
180     {
181       zlog_warn ("%s: could not initialize the socket", __func__);
182       goto end;
183     }
184
185   /* Assign Rx and Tx callbacks are based on real if type */
186   if (if_is_broadcast (circuit->interface))
187     {
188       circuit->tx = isis_send_pdu_bcast;
189       circuit->rx = isis_recv_pdu_bcast;
190     }
191   else if (if_is_pointopoint (circuit->interface))
192     {
193       circuit->tx = isis_send_pdu_p2p;
194       circuit->rx = isis_recv_pdu_p2p;
195     }
196   else
197     {
198       zlog_warn ("isis_sock_init(): unknown circuit type");
199       retval = ISIS_WARNING;
200       goto end;
201     }
202
203 end:
204   if (isisd_privs.change (ZPRIVS_LOWER))
205     zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
206
207   return retval;
208 }
209
210 static inline int
211 llc_check (u_char * llc)
212 {
213   if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3)
214     return 0;
215
216   return 1;
217 }
218
219 int
220 isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
221 {
222   int bytesread, addr_len;
223   struct sockaddr_ll s_addr;
224   u_char llc[LLC_LEN];
225
226   addr_len = sizeof (s_addr);
227
228   memset (&s_addr, 0, sizeof (struct sockaddr_ll));
229
230   bytesread = recvfrom (circuit->fd, (void *) &llc,
231                         LLC_LEN, MSG_PEEK,
232                         (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
233
234   if ((bytesread < 0) || (s_addr.sll_ifindex != (int)circuit->interface->ifindex))
235     {
236       if (bytesread < 0)
237         {
238           zlog_warn ("isis_recv_packet_bcast(): ifname %s, fd %d, "
239                      "bytesread %d, recvfrom(): %s",
240                      circuit->interface->name, circuit->fd, bytesread,
241                      safe_strerror (errno));
242         }
243       if (s_addr.sll_ifindex != (int)circuit->interface->ifindex)
244         {
245           zlog_warn("packet is received on multiple interfaces: "
246                     "socket interface %d, circuit interface %d, "
247                     "packet type %u",
248                     s_addr.sll_ifindex, circuit->interface->ifindex,
249                     s_addr.sll_pkttype);
250         }
251
252       /* get rid of the packet */
253       bytesread = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff),
254                             MSG_DONTWAIT, (struct sockaddr *) &s_addr,
255                             (socklen_t *) &addr_len);
256       return ISIS_WARNING;
257     }
258   /*
259    * Filtering by llc field, discard packets sent by this host (other circuit)
260    */
261   if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING)
262     {
263       /*  Read the packet into discard buff */
264       bytesread = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff),
265                             MSG_DONTWAIT, (struct sockaddr *) &s_addr,
266                             (socklen_t *) &addr_len);
267       if (bytesread < 0)
268         zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
269       return ISIS_WARNING;
270     }
271
272   /* on lan we have to read to the static buff first */
273   bytesread = recvfrom (circuit->fd, sock_buff, sizeof (sock_buff), MSG_DONTWAIT,
274                         (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
275   if (bytesread < 0)
276     {
277       zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
278       return ISIS_WARNING;
279     }
280
281   /* then we lose the LLC */
282   stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
283
284   memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
285
286   return ISIS_OK;
287 }
288
289 int
290 isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
291 {
292   int bytesread, addr_len;
293   struct sockaddr_ll s_addr;
294
295   memset (&s_addr, 0, sizeof (struct sockaddr_ll));
296   addr_len = sizeof (s_addr);
297
298   /* we can read directly to the stream */
299   bytesread = stream_recvfrom (circuit->rcv_stream, circuit->fd,
300                                circuit->interface->mtu, 0,
301                                (struct sockaddr *) &s_addr, 
302                                (socklen_t *) &addr_len);
303
304   if (s_addr.sll_pkttype == PACKET_OUTGOING)
305     {
306       /*  Read the packet into discard buff */
307       bytesread = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff),
308                             MSG_DONTWAIT, (struct sockaddr *) &s_addr,
309                             (socklen_t *) &addr_len);
310       if (bytesread < 0)
311         zlog_warn ("isis_recv_pdu_p2p(): recvfrom() failed");
312       return ISIS_WARNING;
313     }
314
315   /* If we don't have protocol type 0x00FE which is
316    * ISO over GRE we exit with pain :)
317    */
318   if (ntohs (s_addr.sll_protocol) != 0x00FE)
319     {
320       zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
321                  ntohs (s_addr.sll_protocol));
322       return ISIS_WARNING;
323     }
324
325   memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
326
327   return ISIS_OK;
328 }
329
330 int
331 isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
332 {
333   struct msghdr msg;
334   struct iovec iov[2];
335
336   /* we need to do the LLC in here because of P2P circuits, which will
337    * not need it
338    */
339   struct sockaddr_ll sa;
340
341   stream_set_getp (circuit->snd_stream, 0);
342   memset (&sa, 0, sizeof (struct sockaddr_ll));
343   sa.sll_family = AF_PACKET;
344   sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
345   sa.sll_ifindex = circuit->interface->ifindex;
346   sa.sll_halen = ETH_ALEN;
347   /* RFC5309 section 4.1 recommends ALL_ISS */
348   if (circuit->circ_type == CIRCUIT_T_P2P)
349     memcpy (&sa.sll_addr, ALL_ISS, ETH_ALEN);
350   else if (level == 1)
351     memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
352   else
353     memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
354
355   /* on a broadcast circuit */
356   /* first we put the LLC in */
357   sock_buff[0] = 0xFE;
358   sock_buff[1] = 0xFE;
359   sock_buff[2] = 0x03;
360
361   memset (&msg, 0, sizeof (msg));
362   msg.msg_name = &sa;
363   msg.msg_namelen = sizeof (struct sockaddr_ll);
364   msg.msg_iov = iov;
365   msg.msg_iovlen = 2;
366   iov[0].iov_base = sock_buff;
367   iov[0].iov_len = LLC_LEN;
368   iov[1].iov_base = circuit->snd_stream->data;
369   iov[1].iov_len = stream_get_endp (circuit->snd_stream);
370
371   if (sendmsg(circuit->fd, &msg, 0) < 0)
372     {
373       zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
374                 circuit->interface->name, safe_strerror(errno));
375       if (ERRNO_IO_RETRY(errno))
376         return ISIS_WARNING;
377       return ISIS_ERROR;
378     }
379   return ISIS_OK;
380 }
381
382 int
383 isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
384 {
385   struct sockaddr_ll sa;
386   ssize_t rv;
387
388   stream_set_getp (circuit->snd_stream, 0);
389   memset (&sa, 0, sizeof (struct sockaddr_ll));
390   sa.sll_family = AF_PACKET;
391   sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
392   sa.sll_ifindex = circuit->interface->ifindex;
393   sa.sll_halen = ETH_ALEN;
394   if (level == 1)
395     memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
396   else
397     memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
398
399
400   /* lets try correcting the protocol */
401   sa.sll_protocol = htons (0x00FE);
402   rv = sendto(circuit->fd, circuit->snd_stream->data,
403               stream_get_endp (circuit->snd_stream), 0,
404               (struct sockaddr *) &sa,
405               sizeof (struct sockaddr_ll));
406   if (rv < 0)
407     {
408       zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
409                 circuit->interface->name, safe_strerror(errno));
410       if (ERRNO_IO_RETRY(errno))
411         return ISIS_WARNING;
412       return ISIS_ERROR;
413     }
414   return ISIS_OK;
415 }
416
417 #endif /* ISIS_METHOD == ISIS_METHOD_PFPACKET */