]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_bpf.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / isisd / isis_bpf.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
8bc98059
PJ
2/*
3 * IS-IS Rout(e)ing protocol - isis_bpf.c
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
d62a17ae 6 * Tampere University of Technology
8bc98059 7 * Institute of Communications Engineering
8bc98059
PJ
8 */
9
10#include <zebra.h>
745bf05f 11#if ISIS_METHOD == ISIS_METHOD_BPF
8bc98059
PJ
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"
cfd1f27b 19#include "network.h"
8bc98059
PJ
20#include "stream.h"
21#include "if.h"
38937bd5 22#include "lib_errors.h"
8bc98059 23
8bc98059
PJ
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"
344b4a29 32#include "isisd/isis_pdu.h"
8bc98059
PJ
33
34#include "privs.h"
35
8bc98059 36struct bpf_insn llcfilter[] = {
12386e86
RZ
37 BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
38 ETHER_HDR_LEN), /* check first byte */
d62a17ae 39 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5),
12386e86
RZ
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 */
d7c0a89a 45 BPF_STMT(BPF_RET + BPF_K, (unsigned int)-1),
d62a17ae 46 BPF_STMT(BPF_RET + BPF_K, 0)};
d7c0a89a
QY
47unsigned int readblen = 0;
48uint8_t *readbuff = NULL;
8bc98059
PJ
49
50/*
51 * Table 9 - Architectural constants for use with ISO 8802 subnetworks
52 * ISO 10589 - 8.4.8
53 */
54
2b64873d
DL
55static const uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14};
56static const uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15};
207a1429 57static char sock_buff[16384];
8bc98059 58
d62a17ae 59static int open_bpf_dev(struct isis_circuit *circuit)
8bc98059 60{
d62a17ae 61 int i = 0, fd;
62 char bpfdev[128];
63 struct ifreq ifr;
d7c0a89a 64 unsigned int blen, immediate;
f94d4e70 65#ifdef BIOCSSEESENT
d7c0a89a 66 unsigned int seesent;
f94d4e70 67#endif
d62a17ae 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 }
8bc98059
PJ
111
112#ifdef BIOCSSEESENT
d62a17ae 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 }
8bc98059
PJ
120#endif
121
d62a17ae 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 */
6006b807 151 memset(&bpf_prog, 0, sizeof(bpf_prog));
d62a17ae 152 bpf_prog.bf_len = 8;
153 bpf_prog.bf_insns = &(llcfilter[0]);
154 if (ioctl(fd, BIOCSETF, (caddr_t)&bpf_prog) < 0) {
1f46f33f 155 zlog_warn("%s: failed to install filter: %s", __func__,
d62a17ae 156 safe_strerror(errno));
157 return ISIS_WARNING;
8bc98059 158 }
d62a17ae 159
160 assert(fd > 0);
161
162 circuit->fd = fd;
163
164 return ISIS_OK;
8bc98059
PJ
165}
166
167/*
168 * Create the socket and set the tx/rx funcs
169 */
d62a17ae 170int isis_sock_init(struct isis_circuit *circuit)
8bc98059 171{
d62a17ae 172 int retval = ISIS_OK;
173
0cf6db21 174 frr_with_privs(&isisd_privs) {
d62a17ae 175
01b9e3fd 176 retval = open_bpf_dev(circuit);
d62a17ae 177
01b9e3fd 178 if (retval != ISIS_OK) {
633fc9b1
DL
179 zlog_warn("%s: could not initialize the socket",
180 __func__);
01b9e3fd
DL
181 break;
182 }
d62a17ae 183
01b9e3fd
DL
184 if (if_is_broadcast(circuit->interface)) {
185 circuit->tx = isis_send_pdu_bcast;
186 circuit->rx = isis_recv_pdu_bcast;
187 } else {
1f46f33f 188 zlog_warn("%s: unknown circuit type", __func__);
01b9e3fd
DL
189 retval = ISIS_WARNING;
190 break;
191 }
633fc9b1 192 }
8bc98059 193
d62a17ae 194 return retval;
8bc98059
PJ
195}
196
d7c0a89a 197int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa)
8bc98059 198{
55b2b5ab 199 int bytesread = 0, bytestoread = 0, offset, one = 1;
d7c0a89a 200 uint8_t *buff_ptr;
d62a17ae 201 struct bpf_hdr *bpf_hdr;
8bc98059 202
d62a17ae 203 assert(circuit->fd > 0);
8bc98059 204
d62a17ae 205 if (ioctl(circuit->fd, FIONREAD, (caddr_t)&bytestoread) < 0) {
206 zlog_warn("ioctl() FIONREAD failed: %s", safe_strerror(errno));
207 }
8bc98059 208
d62a17ae 209 if (bytestoread) {
210 bytesread = read(circuit->fd, readbuff, readblen);
211 }
212 if (bytesread < 0) {
1f46f33f 213 zlog_warn("%s: read() failed: %s", __func__,
214 safe_strerror(errno));
d62a17ae 215 return ISIS_WARNING;
216 }
8bc98059 217
d62a17ae 218 if (bytesread == 0)
219 return ISIS_WARNING;
8bc98059 220
b9347997 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;
8bc98059 226
b9347997 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);
8bc98059 231
b9347997 232 memcpy(ssnpa, buff_ptr + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN,
233 ETHER_ADDR_LEN);
8bc98059 234
3009394b 235 isis_handle_pdu(circuit, ssnpa);
b9347997 236 stream_reset(circuit->rcv_stream);
237 buff_ptr += BPF_WORDALIGN(bpf_hdr->bh_hdrlen +
238 bpf_hdr->bh_datalen);
239 }
8bc98059 240
8bc98059 241
d62a17ae 242 if (ioctl(circuit->fd, BIOCFLUSH, &one) < 0)
243 zlog_warn("Flushing failed: %s", safe_strerror(errno));
8bc98059 244
d62a17ae 245 return ISIS_OK;
8bc98059
PJ
246}
247
d62a17ae 248int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
8bc98059 249{
d62a17ae 250 struct ether_header *eth;
251 ssize_t written;
252 size_t buflen;
253
12386e86 254 buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN;
d62a17ae 255 if (buflen > sizeof(sock_buff)) {
256 zlog_warn(
1f46f33f 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);
d62a17ae 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)
d1be6968 270 memcpy(eth->ether_dhost, ALL_L1_ISS, ETH_ALEN);
d62a17ae 271 else
d1be6968
DS
272 memcpy(eth->ether_dhost, ALL_L2_ISS, ETH_ALEN);
273 memcpy(eth->ether_shost, circuit->u.bc.snpa, ETH_ALEN);
d62a17ae 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 */
12386e86
RZ
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;
d62a17ae 283
284 /* then we copy the data */
12386e86 285 memcpy(sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
d62a17ae 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;
8bc98059
PJ
299}
300
745bf05f 301#endif /* ISIS_METHOD == ISIS_METHOD_BPF */