2 * IS-IS Rout(e)ing protocol - isis_pfpacket.c
4 * Copyright (C) 2001,2002 Sampo Saaristo
5 * Tampere University of Technology
6 * Institute of Communications Engineering
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)
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
18 * You should have received a copy of the GNU General Public License along
19 * with this program; see the file COPYING; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #if ISIS_METHOD == ISIS_METHOD_PFPACKET
25 #include <net/ethernet.h> /* the L2 protocols */
26 #include <netpacket/packet.h>
28 #include <linux/filter.h>
35 #include "isisd/dict.h"
36 #include "isisd/isis_constants.h"
37 #include "isisd/isis_common.h"
38 #include "isisd/isis_circuit.h"
39 #include "isisd/isis_flags.h"
40 #include "isisd/isisd.h"
41 #include "isisd/isis_constants.h"
42 #include "isisd/isis_circuit.h"
43 #include "isisd/isis_network.h"
47 extern struct zebra_privs_t isisd_privs
;
49 /* tcpdump -i eth0 'isis' -dd */
50 static struct sock_filter isisfilter
[] = {
51 /* NB: we're in SOCK_DGRAM, so src/dst mac + length are stripped off!
52 * (OTOH it's a bit more lower-layer agnostic and might work over GRE?) */
53 /* { 0x28, 0, 0, 0x0000000c - 14 }, */
54 /* { 0x25, 5, 0, 0x000005dc }, */
55 { 0x28, 0, 0, 0x0000000e - 14 },
56 { 0x15, 0, 3, 0x0000fefe },
57 { 0x30, 0, 0, 0x00000011 - 14 },
58 { 0x15, 0, 1, 0x00000083 },
59 { 0x6, 0, 0, 0x00040000 },
60 { 0x6, 0, 0, 0x00000000 },
63 static struct sock_fprog bpf
= {
64 .len
= array_size(isisfilter
),
69 * Table 9 - Architectural constants for use with ISO 8802 subnetworks
73 u_char ALL_L1_ISS
[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 };
74 u_char ALL_L2_ISS
[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 };
75 u_char ALL_ISS
[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 };
76 u_char ALL_ESS
[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 };
78 static uint8_t discard_buff
[8192];
79 static uint8_t sock_buff
[8192];
82 * if level is 0 we are joining p2p multicast
83 * FIXME: and the p2p multicast being ???
86 isis_multicast_join (int fd
, int registerto
, int if_num
)
88 struct packet_mreq mreq
;
90 memset (&mreq
, 0, sizeof (mreq
));
91 mreq
.mr_ifindex
= if_num
;
94 mreq
.mr_type
= PACKET_MR_MULTICAST
;
95 mreq
.mr_alen
= ETH_ALEN
;
97 memcpy (&mreq
.mr_address
, ALL_L1_ISS
, ETH_ALEN
);
98 else if (registerto
== 2)
99 memcpy (&mreq
.mr_address
, ALL_L2_ISS
, ETH_ALEN
);
100 else if (registerto
== 3)
101 memcpy (&mreq
.mr_address
, ALL_ISS
, ETH_ALEN
);
103 memcpy (&mreq
.mr_address
, ALL_ESS
, ETH_ALEN
);
108 mreq
.mr_type
= PACKET_MR_ALLMULTI
;
111 zlog_debug ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
112 "address = %02x:%02x:%02x:%02x:%02x:%02x",
113 fd
, registerto
, if_num
, mreq
.mr_address
[0], mreq
.mr_address
[1],
114 mreq
.mr_address
[2], mreq
.mr_address
[3], mreq
.mr_address
[4],
116 #endif /* EXTREME_DEBUG */
117 if (setsockopt (fd
, SOL_PACKET
, PACKET_ADD_MEMBERSHIP
, &mreq
,
118 sizeof (struct packet_mreq
)))
120 zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno
));
128 open_packet_socket (struct isis_circuit
*circuit
)
130 struct sockaddr_ll s_addr
;
131 int fd
, retval
= ISIS_OK
;
133 fd
= socket (PF_PACKET
, SOCK_DGRAM
, htons (ETH_P_ALL
));
136 zlog_warn ("open_packet_socket(): socket() failed %s",
137 safe_strerror (errno
));
141 if (setsockopt (fd
, SOL_SOCKET
, SO_ATTACH_FILTER
, &bpf
, sizeof (bpf
)))
143 zlog_warn ("open_packet_socket(): SO_ATTACH_FILTER failed: %s",
144 safe_strerror (errno
));
148 * Bind to the physical interface
150 memset (&s_addr
, 0, sizeof (struct sockaddr_ll
));
151 s_addr
.sll_family
= AF_PACKET
;
152 s_addr
.sll_protocol
= htons (ETH_P_ALL
);
153 s_addr
.sll_ifindex
= circuit
->interface
->ifindex
;
155 if (bind (fd
, (struct sockaddr
*) (&s_addr
),
156 sizeof (struct sockaddr_ll
)) < 0)
158 zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno
));
165 if (if_is_broadcast (circuit
->interface
))
168 * Join to multicast groups
170 * 8.4.2 - Broadcast subnetwork IIH PDUs
171 * FIXME: is there a case only one will fail??
173 /* joining ALL_L1_ISS */
174 retval
|= isis_multicast_join (circuit
->fd
, 1,
175 circuit
->interface
->ifindex
);
176 /* joining ALL_L2_ISS */
177 retval
|= isis_multicast_join (circuit
->fd
, 2,
178 circuit
->interface
->ifindex
);
179 /* joining ALL_ISS (used in RFC 5309 p2p-over-lan as well) */
180 retval
|= isis_multicast_join (circuit
->fd
, 3,
181 circuit
->interface
->ifindex
);
186 isis_multicast_join (circuit
->fd
, 0, circuit
->interface
->ifindex
);
193 * Create the socket and set the tx/rx funcs
196 isis_sock_init (struct isis_circuit
*circuit
)
198 int retval
= ISIS_OK
;
200 if (isisd_privs
.change (ZPRIVS_RAISE
))
201 zlog_err ("%s: could not raise privs, %s", __func__
, safe_strerror (errno
));
203 retval
= open_packet_socket (circuit
);
205 if (retval
!= ISIS_OK
)
207 zlog_warn ("%s: could not initialize the socket", __func__
);
211 /* Assign Rx and Tx callbacks are based on real if type */
212 if (if_is_broadcast (circuit
->interface
))
214 circuit
->tx
= isis_send_pdu_bcast
;
215 circuit
->rx
= isis_recv_pdu_bcast
;
217 else if (if_is_pointopoint (circuit
->interface
))
219 circuit
->tx
= isis_send_pdu_p2p
;
220 circuit
->rx
= isis_recv_pdu_p2p
;
224 zlog_warn ("isis_sock_init(): unknown circuit type");
225 retval
= ISIS_WARNING
;
230 if (isisd_privs
.change (ZPRIVS_LOWER
))
231 zlog_err ("%s: could not lower privs, %s", __func__
, safe_strerror (errno
));
237 llc_check (u_char
* llc
)
239 if (*llc
!= ISO_SAP
|| *(llc
+ 1) != ISO_SAP
|| *(llc
+ 2) != 3)
246 isis_recv_pdu_bcast (struct isis_circuit
*circuit
, u_char
* ssnpa
)
248 int bytesread
, addr_len
;
249 struct sockaddr_ll s_addr
;
252 addr_len
= sizeof (s_addr
);
254 memset (&s_addr
, 0, sizeof (struct sockaddr_ll
));
256 bytesread
= recvfrom (circuit
->fd
, (void *) &llc
,
258 (struct sockaddr
*) &s_addr
, (socklen_t
*) &addr_len
);
260 if ((bytesread
< 0) || (s_addr
.sll_ifindex
!= (int)circuit
->interface
->ifindex
))
264 zlog_warn ("isis_recv_packet_bcast(): ifname %s, fd %d, "
265 "bytesread %d, recvfrom(): %s",
266 circuit
->interface
->name
, circuit
->fd
, bytesread
,
267 safe_strerror (errno
));
269 if (s_addr
.sll_ifindex
!= (int)circuit
->interface
->ifindex
)
271 zlog_warn("packet is received on multiple interfaces: "
272 "socket interface %d, circuit interface %d, "
274 s_addr
.sll_ifindex
, circuit
->interface
->ifindex
,
278 /* get rid of the packet */
279 bytesread
= recvfrom (circuit
->fd
, discard_buff
, sizeof (discard_buff
),
280 MSG_DONTWAIT
, (struct sockaddr
*) &s_addr
,
281 (socklen_t
*) &addr_len
);
284 zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
289 * Filtering by llc field, discard packets sent by this host (other circuit)
291 if (!llc_check (llc
) || s_addr
.sll_pkttype
== PACKET_OUTGOING
)
293 /* Read the packet into discard buff */
294 bytesread
= recvfrom (circuit
->fd
, discard_buff
, sizeof (discard_buff
),
295 MSG_DONTWAIT
, (struct sockaddr
*) &s_addr
,
296 (socklen_t
*) &addr_len
);
298 zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
302 /* on lan we have to read to the static buff first */
303 bytesread
= recvfrom (circuit
->fd
, sock_buff
, sizeof (sock_buff
), MSG_DONTWAIT
,
304 (struct sockaddr
*) &s_addr
, (socklen_t
*) &addr_len
);
307 zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
311 /* then we lose the LLC */
312 stream_write (circuit
->rcv_stream
, sock_buff
+ LLC_LEN
, bytesread
- LLC_LEN
);
314 memcpy (ssnpa
, &s_addr
.sll_addr
, s_addr
.sll_halen
);
320 isis_recv_pdu_p2p (struct isis_circuit
*circuit
, u_char
* ssnpa
)
322 int bytesread
, addr_len
;
323 struct sockaddr_ll s_addr
;
325 memset (&s_addr
, 0, sizeof (struct sockaddr_ll
));
326 addr_len
= sizeof (s_addr
);
328 /* we can read directly to the stream */
329 stream_recvfrom (circuit
->rcv_stream
, circuit
->fd
,
330 circuit
->interface
->mtu
, 0,
331 (struct sockaddr
*) &s_addr
,
332 (socklen_t
*) &addr_len
);
334 if (s_addr
.sll_pkttype
== PACKET_OUTGOING
)
336 /* Read the packet into discard buff */
337 bytesread
= recvfrom (circuit
->fd
, discard_buff
, sizeof (discard_buff
),
338 MSG_DONTWAIT
, (struct sockaddr
*) &s_addr
,
339 (socklen_t
*) &addr_len
);
341 zlog_warn ("isis_recv_pdu_p2p(): recvfrom() failed");
345 /* If we don't have protocol type 0x00FE which is
346 * ISO over GRE we exit with pain :)
348 if (ntohs (s_addr
.sll_protocol
) != 0x00FE)
350 zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
351 ntohs (s_addr
.sll_protocol
));
355 memcpy (ssnpa
, &s_addr
.sll_addr
, s_addr
.sll_halen
);
361 isis_send_pdu_bcast (struct isis_circuit
*circuit
, int level
)
366 /* we need to do the LLC in here because of P2P circuits, which will
369 struct sockaddr_ll sa
;
371 stream_set_getp (circuit
->snd_stream
, 0);
372 memset (&sa
, 0, sizeof (struct sockaddr_ll
));
373 sa
.sll_family
= AF_PACKET
;
375 size_t frame_size
= stream_get_endp(circuit
->snd_stream
) + LLC_LEN
;
376 sa
.sll_protocol
= htons(isis_ethertype(frame_size
));
377 sa
.sll_ifindex
= circuit
->interface
->ifindex
;
378 sa
.sll_halen
= ETH_ALEN
;
379 /* RFC5309 section 4.1 recommends ALL_ISS */
380 if (circuit
->circ_type
== CIRCUIT_T_P2P
)
381 memcpy (&sa
.sll_addr
, ALL_ISS
, ETH_ALEN
);
383 memcpy (&sa
.sll_addr
, ALL_L1_ISS
, ETH_ALEN
);
385 memcpy (&sa
.sll_addr
, ALL_L2_ISS
, ETH_ALEN
);
387 /* on a broadcast circuit */
388 /* first we put the LLC in */
393 memset (&msg
, 0, sizeof (msg
));
395 msg
.msg_namelen
= sizeof (struct sockaddr_ll
);
398 iov
[0].iov_base
= sock_buff
;
399 iov
[0].iov_len
= LLC_LEN
;
400 iov
[1].iov_base
= circuit
->snd_stream
->data
;
401 iov
[1].iov_len
= stream_get_endp (circuit
->snd_stream
);
403 if (sendmsg(circuit
->fd
, &msg
, 0) < 0)
405 zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
406 circuit
->interface
->name
, safe_strerror(errno
));
407 if (ERRNO_IO_RETRY(errno
))
415 isis_send_pdu_p2p (struct isis_circuit
*circuit
, int level
)
417 struct sockaddr_ll sa
;
420 stream_set_getp (circuit
->snd_stream
, 0);
421 memset (&sa
, 0, sizeof (struct sockaddr_ll
));
422 sa
.sll_family
= AF_PACKET
;
423 sa
.sll_ifindex
= circuit
->interface
->ifindex
;
424 sa
.sll_halen
= ETH_ALEN
;
426 memcpy (&sa
.sll_addr
, ALL_L1_ISS
, ETH_ALEN
);
428 memcpy (&sa
.sll_addr
, ALL_L2_ISS
, ETH_ALEN
);
431 /* lets try correcting the protocol */
432 sa
.sll_protocol
= htons (0x00FE);
433 rv
= sendto(circuit
->fd
, circuit
->snd_stream
->data
,
434 stream_get_endp (circuit
->snd_stream
), 0,
435 (struct sockaddr
*) &sa
,
436 sizeof (struct sockaddr_ll
));
439 zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
440 circuit
->interface
->name
, safe_strerror(errno
));
441 if (ERRNO_IO_RETRY(errno
))
448 #endif /* ISIS_METHOD == ISIS_METHOD_PFPACKET */