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