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