]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_bpf.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / isisd / isis_bpf.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * IS-IS Rout(e)ing protocol - isis_bpf.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_BPF
12 #include <net/if.h>
13 #include <netinet/if_ether.h>
14 #include <sys/time.h>
15 #include <sys/ioctl.h>
16 #include <net/bpf.h>
17
18 #include "log.h"
19 #include "network.h"
20 #include "stream.h"
21 #include "if.h"
22 #include "lib_errors.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 #include "isisd/isis_pdu.h"
33
34 #include "privs.h"
35
36 struct bpf_insn llcfilter[] = {
37 BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
38 ETHER_HDR_LEN), /* check first byte */
39 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5),
40 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1),
41 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0,
42 3), /* check second byte */
43 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2),
44 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), /* check third byte */
45 BPF_STMT(BPF_RET + BPF_K, (unsigned int)-1),
46 BPF_STMT(BPF_RET + BPF_K, 0)};
47 unsigned int readblen = 0;
48 uint8_t *readbuff = NULL;
49
50 /*
51 * Table 9 - Architectural constants for use with ISO 8802 subnetworks
52 * ISO 10589 - 8.4.8
53 */
54
55 static const uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14};
56 static const uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15};
57 static char sock_buff[16384];
58
59 static int open_bpf_dev(struct isis_circuit *circuit)
60 {
61 int i = 0, fd;
62 char bpfdev[128];
63 struct ifreq ifr;
64 unsigned int blen, immediate;
65 #ifdef BIOCSSEESENT
66 unsigned int seesent;
67 #endif
68 struct timeval timeout;
69 struct bpf_program bpf_prog;
70
71 do {
72 (void)snprintf(bpfdev, sizeof(bpfdev), "/dev/bpf%d", i++);
73 fd = open(bpfdev, O_RDWR);
74 } while (fd < 0 && errno == EBUSY);
75
76 if (fd < 0) {
77 zlog_warn("open_bpf_dev(): failed to create bpf socket: %s",
78 safe_strerror(errno));
79 return ISIS_WARNING;
80 }
81
82 zlog_debug("Opened BPF device %s", bpfdev);
83
84 memcpy(ifr.ifr_name, circuit->interface->name, sizeof(ifr.ifr_name));
85 if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
86 zlog_warn("open_bpf_dev(): failed to bind to interface: %s",
87 safe_strerror(errno));
88 return ISIS_WARNING;
89 }
90
91 if (ioctl(fd, BIOCGBLEN, (caddr_t)&blen) < 0) {
92 zlog_warn("failed to get BPF buffer len");
93 blen = circuit->interface->mtu;
94 }
95
96 readblen = blen;
97
98 if (readbuff == NULL)
99 readbuff = malloc(blen);
100
101 zlog_debug("BPF buffer len = %u", blen);
102
103 /* BPF(4): reads return immediately upon packet reception.
104 * Otherwise, a read will block until either the kernel
105 * buffer becomes full or a timeout occurs.
106 */
107 immediate = 1;
108 if (ioctl(fd, BIOCIMMEDIATE, (caddr_t)&immediate) < 0) {
109 zlog_warn("failed to set BPF dev to immediate mode");
110 }
111
112 #ifdef BIOCSSEESENT
113 /*
114 * We want to see only incoming packets
115 */
116 seesent = 0;
117 if (ioctl(fd, BIOCSSEESENT, (caddr_t)&seesent) < 0) {
118 zlog_warn("failed to set BPF dev to incoming only mode");
119 }
120 #endif
121
122 /*
123 * ...but all of them
124 */
125 if (ioctl(fd, BIOCPROMISC) < 0) {
126 zlog_warn("failed to set BPF dev to promiscuous mode");
127 }
128
129 /*
130 * If the buffer length is smaller than our mtu, lets try to increase it
131 */
132 if (blen < circuit->interface->mtu) {
133 if (ioctl(fd, BIOCSBLEN, &circuit->interface->mtu) < 0) {
134 zlog_warn("failed to set BPF buffer len (%u to %u)",
135 blen, circuit->interface->mtu);
136 }
137 }
138
139 /*
140 * Set a timeout parameter - hope this helps select()
141 */
142 timeout.tv_sec = 600;
143 timeout.tv_usec = 0;
144 if (ioctl(fd, BIOCSRTIMEOUT, (caddr_t)&timeout) < 0) {
145 zlog_warn("failed to set BPF device timeout");
146 }
147
148 /*
149 * And set the filter
150 */
151 memset(&bpf_prog, 0, sizeof(bpf_prog));
152 bpf_prog.bf_len = 8;
153 bpf_prog.bf_insns = &(llcfilter[0]);
154 if (ioctl(fd, BIOCSETF, (caddr_t)&bpf_prog) < 0) {
155 zlog_warn("%s: failed to install filter: %s", __func__,
156 safe_strerror(errno));
157 return ISIS_WARNING;
158 }
159
160 assert(fd > 0);
161
162 circuit->fd = fd;
163
164 return ISIS_OK;
165 }
166
167 /*
168 * Create the socket and set the tx/rx funcs
169 */
170 int isis_sock_init(struct isis_circuit *circuit)
171 {
172 int retval = ISIS_OK;
173
174 frr_with_privs(&isisd_privs) {
175
176 retval = open_bpf_dev(circuit);
177
178 if (retval != ISIS_OK) {
179 zlog_warn("%s: could not initialize the socket",
180 __func__);
181 break;
182 }
183
184 if (if_is_broadcast(circuit->interface)) {
185 circuit->tx = isis_send_pdu_bcast;
186 circuit->rx = isis_recv_pdu_bcast;
187 } else {
188 zlog_warn("%s: unknown circuit type", __func__);
189 retval = ISIS_WARNING;
190 break;
191 }
192 }
193
194 return retval;
195 }
196
197 int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa)
198 {
199 int bytesread = 0, bytestoread = 0, offset, one = 1;
200 uint8_t *buff_ptr;
201 struct bpf_hdr *bpf_hdr;
202
203 assert(circuit->fd > 0);
204
205 if (ioctl(circuit->fd, FIONREAD, (caddr_t)&bytestoread) < 0) {
206 zlog_warn("ioctl() FIONREAD failed: %s", safe_strerror(errno));
207 }
208
209 if (bytestoread) {
210 bytesread = read(circuit->fd, readbuff, readblen);
211 }
212 if (bytesread < 0) {
213 zlog_warn("%s: read() failed: %s", __func__,
214 safe_strerror(errno));
215 return ISIS_WARNING;
216 }
217
218 if (bytesread == 0)
219 return ISIS_WARNING;
220
221 buff_ptr = readbuff;
222 while (buff_ptr < readbuff + bytesread) {
223 bpf_hdr = (struct bpf_hdr *) buff_ptr;
224 assert(bpf_hdr->bh_caplen == bpf_hdr->bh_datalen);
225 offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN;
226
227 /* then we lose the BPF, LLC and ethernet headers */
228 stream_write(circuit->rcv_stream, buff_ptr + offset,
229 bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN);
230 stream_set_getp(circuit->rcv_stream, 0);
231
232 memcpy(ssnpa, buff_ptr + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN,
233 ETHER_ADDR_LEN);
234
235 isis_handle_pdu(circuit, ssnpa);
236 stream_reset(circuit->rcv_stream);
237 buff_ptr += BPF_WORDALIGN(bpf_hdr->bh_hdrlen +
238 bpf_hdr->bh_datalen);
239 }
240
241
242 if (ioctl(circuit->fd, BIOCFLUSH, &one) < 0)
243 zlog_warn("Flushing failed: %s", safe_strerror(errno));
244
245 return ISIS_OK;
246 }
247
248 int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
249 {
250 struct ether_header *eth;
251 ssize_t written;
252 size_t buflen;
253
254 buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN;
255 if (buflen > sizeof(sock_buff)) {
256 zlog_warn(
257 "%s: sock_buff size %zu is less than output pdu size %zu on circuit %s",
258 __func__, sizeof(sock_buff), buflen,
259 circuit->interface->name);
260 return ISIS_WARNING;
261 }
262
263 stream_set_getp(circuit->snd_stream, 0);
264
265 /*
266 * First the eth header
267 */
268 eth = (struct ether_header *)sock_buff;
269 if (level == 1)
270 memcpy(eth->ether_dhost, ALL_L1_ISS, ETH_ALEN);
271 else
272 memcpy(eth->ether_dhost, ALL_L2_ISS, ETH_ALEN);
273 memcpy(eth->ether_shost, circuit->u.bc.snpa, ETH_ALEN);
274 size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN;
275 eth->ether_type = htons(isis_ethertype(frame_size));
276
277 /*
278 * Then the LLC
279 */
280 sock_buff[ETHER_HDR_LEN] = ISO_SAP;
281 sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP;
282 sock_buff[ETHER_HDR_LEN + 2] = 0x03;
283
284 /* then we copy the data */
285 memcpy(sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
286 stream_get_endp(circuit->snd_stream));
287
288 /* now we can send this */
289 written = write(circuit->fd, sock_buff, buflen);
290 if (written < 0) {
291 zlog_warn("IS-IS bpf: could not transmit packet on %s: %s",
292 circuit->interface->name, safe_strerror(errno));
293 if (ERRNO_IO_RETRY(errno))
294 return ISIS_WARNING;
295 return ISIS_ERROR;
296 }
297
298 return ISIS_OK;
299 }
300
301 #endif /* ISIS_METHOD == ISIS_METHOD_BPF */