]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_pfpacket.c
Merge remote-tracking branch 'origin/stable/2.0'
[mirror_frr.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; see the file COPYING; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 <linux/filter.h>
29
30 #include "log.h"
31 #include "network.h"
32 #include "stream.h"
33 #include "if.h"
34
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"
44
45 #include "privs.h"
46
47 extern struct zebra_privs_t isisd_privs;
48
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 },
61 };
62
63 static struct sock_fprog bpf = {
64 .len = array_size(isisfilter),
65 .filter = isisfilter,
66 };
67
68 /*
69 * Table 9 - Architectural constants for use with ISO 8802 subnetworks
70 * ISO 10589 - 8.4.8
71 */
72
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 };
77
78 static uint8_t discard_buff[8192];
79 static uint8_t sock_buff[8192];
80
81 /*
82 * if level is 0 we are joining p2p multicast
83 * FIXME: and the p2p multicast being ???
84 */
85 static int
86 isis_multicast_join (int fd, int registerto, int if_num)
87 {
88 struct packet_mreq mreq;
89
90 memset (&mreq, 0, sizeof (mreq));
91 mreq.mr_ifindex = if_num;
92 if (registerto)
93 {
94 mreq.mr_type = PACKET_MR_MULTICAST;
95 mreq.mr_alen = ETH_ALEN;
96 if (registerto == 1)
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);
102 else
103 memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN);
104
105 }
106 else
107 {
108 mreq.mr_type = PACKET_MR_ALLMULTI;
109 }
110 #ifdef EXTREME_DEBUG
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],
115 mreq.mr_address[5]);
116 #endif /* EXTREME_DEBUG */
117 if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
118 sizeof (struct packet_mreq)))
119 {
120 zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno));
121 return ISIS_WARNING;
122 }
123
124 return ISIS_OK;
125 }
126
127 static int
128 open_packet_socket (struct isis_circuit *circuit)
129 {
130 struct sockaddr_ll s_addr;
131 int fd, retval = ISIS_OK;
132
133 fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
134 if (fd < 0)
135 {
136 zlog_warn ("open_packet_socket(): socket() failed %s",
137 safe_strerror (errno));
138 return ISIS_WARNING;
139 }
140
141 if (setsockopt (fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof (bpf)))
142 {
143 zlog_warn ("open_packet_socket(): SO_ATTACH_FILTER failed: %s",
144 safe_strerror (errno));
145 }
146
147 /*
148 * Bind to the physical interface
149 */
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;
154
155 if (bind (fd, (struct sockaddr *) (&s_addr),
156 sizeof (struct sockaddr_ll)) < 0)
157 {
158 zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno));
159 close (fd);
160 return ISIS_WARNING;
161 }
162
163 circuit->fd = fd;
164
165 if (if_is_broadcast (circuit->interface))
166 {
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 /* 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);
182 }
183 else
184 {
185 retval =
186 isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex);
187 }
188
189 return retval;
190 }
191
192 /*
193 * Create the socket and set the tx/rx funcs
194 */
195 int
196 isis_sock_init (struct isis_circuit *circuit)
197 {
198 int retval = ISIS_OK;
199
200 if (isisd_privs.change (ZPRIVS_RAISE))
201 zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno));
202
203 retval = open_packet_socket (circuit);
204
205 if (retval != ISIS_OK)
206 {
207 zlog_warn ("%s: could not initialize the socket", __func__);
208 goto end;
209 }
210
211 /* Assign Rx and Tx callbacks are based on real if type */
212 if (if_is_broadcast (circuit->interface))
213 {
214 circuit->tx = isis_send_pdu_bcast;
215 circuit->rx = isis_recv_pdu_bcast;
216 }
217 else if (if_is_pointopoint (circuit->interface))
218 {
219 circuit->tx = isis_send_pdu_p2p;
220 circuit->rx = isis_recv_pdu_p2p;
221 }
222 else
223 {
224 zlog_warn ("isis_sock_init(): unknown circuit type");
225 retval = ISIS_WARNING;
226 goto end;
227 }
228
229 end:
230 if (isisd_privs.change (ZPRIVS_LOWER))
231 zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno));
232
233 return retval;
234 }
235
236 static inline int
237 llc_check (u_char * llc)
238 {
239 if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3)
240 return 0;
241
242 return 1;
243 }
244
245 int
246 isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
247 {
248 int bytesread, addr_len;
249 struct sockaddr_ll s_addr;
250 u_char llc[LLC_LEN];
251
252 addr_len = sizeof (s_addr);
253
254 memset (&s_addr, 0, sizeof (struct sockaddr_ll));
255
256 bytesread = recvfrom (circuit->fd, (void *) &llc,
257 LLC_LEN, MSG_PEEK,
258 (struct sockaddr *) &s_addr, (socklen_t *) &addr_len);
259
260 if ((bytesread < 0) || (s_addr.sll_ifindex != (int)circuit->interface->ifindex))
261 {
262 if (bytesread < 0)
263 {
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));
268 }
269 if (s_addr.sll_ifindex != (int)circuit->interface->ifindex)
270 {
271 zlog_warn("packet is received on multiple interfaces: "
272 "socket interface %d, circuit interface %d, "
273 "packet type %u",
274 s_addr.sll_ifindex, circuit->interface->ifindex,
275 s_addr.sll_pkttype);
276 }
277
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);
282
283 if (bytesread < 0)
284 zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
285
286 return ISIS_WARNING;
287 }
288 /*
289 * Filtering by llc field, discard packets sent by this host (other circuit)
290 */
291 if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING)
292 {
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);
297 if (bytesread < 0)
298 zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
299 return ISIS_WARNING;
300 }
301
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);
305 if (bytesread < 0)
306 {
307 zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed");
308 return ISIS_WARNING;
309 }
310
311 /* then we lose the LLC */
312 stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN);
313
314 memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
315
316 return ISIS_OK;
317 }
318
319 int
320 isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
321 {
322 int bytesread, addr_len;
323 struct sockaddr_ll s_addr;
324
325 memset (&s_addr, 0, sizeof (struct sockaddr_ll));
326 addr_len = sizeof (s_addr);
327
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);
333
334 if (s_addr.sll_pkttype == PACKET_OUTGOING)
335 {
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);
340 if (bytesread < 0)
341 zlog_warn ("isis_recv_pdu_p2p(): recvfrom() failed");
342 return ISIS_WARNING;
343 }
344
345 /* If we don't have protocol type 0x00FE which is
346 * ISO over GRE we exit with pain :)
347 */
348 if (ntohs (s_addr.sll_protocol) != 0x00FE)
349 {
350 zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X",
351 ntohs (s_addr.sll_protocol));
352 return ISIS_WARNING;
353 }
354
355 memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
356
357 return ISIS_OK;
358 }
359
360 int
361 isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
362 {
363 struct msghdr msg;
364 struct iovec iov[2];
365
366 /* we need to do the LLC in here because of P2P circuits, which will
367 * not need it
368 */
369 struct sockaddr_ll sa;
370
371 stream_set_getp (circuit->snd_stream, 0);
372 memset (&sa, 0, sizeof (struct sockaddr_ll));
373 sa.sll_family = AF_PACKET;
374
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);
382 else if (level == 1)
383 memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
384 else
385 memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
386
387 /* on a broadcast circuit */
388 /* first we put the LLC in */
389 sock_buff[0] = 0xFE;
390 sock_buff[1] = 0xFE;
391 sock_buff[2] = 0x03;
392
393 memset (&msg, 0, sizeof (msg));
394 msg.msg_name = &sa;
395 msg.msg_namelen = sizeof (struct sockaddr_ll);
396 msg.msg_iov = iov;
397 msg.msg_iovlen = 2;
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);
402
403 if (sendmsg(circuit->fd, &msg, 0) < 0)
404 {
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))
408 return ISIS_WARNING;
409 return ISIS_ERROR;
410 }
411 return ISIS_OK;
412 }
413
414 int
415 isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
416 {
417 struct sockaddr_ll sa;
418 ssize_t rv;
419
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;
425 if (level == 1)
426 memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
427 else
428 memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
429
430
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));
437 if (rv < 0)
438 {
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))
442 return ISIS_WARNING;
443 return ISIS_ERROR;
444 }
445 return ISIS_OK;
446 }
447
448 #endif /* ISIS_METHOD == ISIS_METHOD_PFPACKET */