]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_pfpacket.c
Merge branch 'stable/3.0' into tmp-3.0-master-merge
[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 {
52 /* NB: we're in SOCK_DGRAM, so src/dst mac + length are stripped
53 * off!
54 * (OTOH it's a bit more lower-layer agnostic and might work
55 * over GRE?) */
56 /* { 0x28, 0, 0, 0x0000000c - 14 }, */
57 /* { 0x25, 5, 0, 0x000005dc }, */
58 {0x28, 0, 0, 0x0000000e - 14}, {0x15, 0, 3, 0x0000fefe},
59 {0x30, 0, 0, 0x00000011 - 14}, {0x15, 0, 1, 0x00000083},
60 {0x6, 0, 0, 0x00040000}, {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 isis_multicast_join(int fd, int registerto, int if_num)
86 {
87 struct packet_mreq mreq;
88
89 memset(&mreq, 0, sizeof(mreq));
90 mreq.mr_ifindex = if_num;
91 if (registerto) {
92 mreq.mr_type = PACKET_MR_MULTICAST;
93 mreq.mr_alen = ETH_ALEN;
94 if (registerto == 1)
95 memcpy(&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
96 else if (registerto == 2)
97 memcpy(&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
98 else if (registerto == 3)
99 memcpy(&mreq.mr_address, ALL_ISS, ETH_ALEN);
100 else
101 memcpy(&mreq.mr_address, ALL_ESS, ETH_ALEN);
102
103 } else {
104 mreq.mr_type = PACKET_MR_ALLMULTI;
105 }
106 #ifdef EXTREME_DEBUG
107 zlog_debug(
108 "isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
109 "address = %02x:%02x:%02x:%02x:%02x:%02x",
110 fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1],
111 mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4],
112 mreq.mr_address[5]);
113 #endif /* EXTREME_DEBUG */
114 if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
115 sizeof(struct packet_mreq))) {
116 zlog_warn("isis_multicast_join(): setsockopt(): %s",
117 safe_strerror(errno));
118 return ISIS_WARNING;
119 }
120
121 return ISIS_OK;
122 }
123
124 static int open_packet_socket(struct isis_circuit *circuit)
125 {
126 struct sockaddr_ll s_addr;
127 int fd, retval = ISIS_OK;
128
129 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
130 if (fd < 0) {
131 zlog_warn("open_packet_socket(): socket() failed %s",
132 safe_strerror(errno));
133 return ISIS_WARNING;
134 }
135
136 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf))) {
137 zlog_warn("open_packet_socket(): SO_ATTACH_FILTER failed: %s",
138 safe_strerror(errno));
139 }
140
141 /*
142 * Bind to the physical interface
143 */
144 memset(&s_addr, 0, sizeof(struct sockaddr_ll));
145 s_addr.sll_family = AF_PACKET;
146 s_addr.sll_protocol = htons(ETH_P_ALL);
147 s_addr.sll_ifindex = circuit->interface->ifindex;
148
149 if (bind(fd, (struct sockaddr *)(&s_addr), sizeof(struct sockaddr_ll))
150 < 0) {
151 zlog_warn("open_packet_socket(): bind() failed: %s",
152 safe_strerror(errno));
153 close(fd);
154 return ISIS_WARNING;
155 }
156
157 circuit->fd = fd;
158
159 if (if_is_broadcast(circuit->interface)) {
160 /*
161 * Join to multicast groups
162 * according to
163 * 8.4.2 - Broadcast subnetwork IIH PDUs
164 * FIXME: is there a case only one will fail??
165 */
166 /* joining ALL_L1_ISS */
167 retval |= isis_multicast_join(circuit->fd, 1,
168 circuit->interface->ifindex);
169 /* joining ALL_L2_ISS */
170 retval |= isis_multicast_join(circuit->fd, 2,
171 circuit->interface->ifindex);
172 /* joining ALL_ISS (used in RFC 5309 p2p-over-lan as well) */
173 retval |= isis_multicast_join(circuit->fd, 3,
174 circuit->interface->ifindex);
175 } else {
176 retval = isis_multicast_join(circuit->fd, 0,
177 circuit->interface->ifindex);
178 }
179
180 return retval;
181 }
182
183 /*
184 * Create the socket and set the tx/rx funcs
185 */
186 int isis_sock_init(struct isis_circuit *circuit)
187 {
188 int retval = ISIS_OK;
189
190 if (isisd_privs.change(ZPRIVS_RAISE))
191 zlog_err("%s: could not raise privs, %s", __func__,
192 safe_strerror(errno));
193
194 retval = open_packet_socket(circuit);
195
196 if (retval != ISIS_OK) {
197 zlog_warn("%s: could not initialize the socket", __func__);
198 goto end;
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("isis_sock_init(): unknown circuit type");
210 retval = ISIS_WARNING;
211 goto end;
212 }
213
214 end:
215 if (isisd_privs.change(ZPRIVS_LOWER))
216 zlog_err("%s: could not lower privs, %s", __func__,
217 safe_strerror(errno));
218
219 return retval;
220 }
221
222 static inline int llc_check(u_char *llc)
223 {
224 if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3)
225 return 0;
226
227 return 1;
228 }
229
230 int isis_recv_pdu_bcast(struct isis_circuit *circuit, u_char *ssnpa)
231 {
232 int bytesread, addr_len;
233 struct sockaddr_ll s_addr;
234 u_char llc[LLC_LEN];
235
236 addr_len = sizeof(s_addr);
237
238 memset(&s_addr, 0, sizeof(struct sockaddr_ll));
239
240 bytesread =
241 recvfrom(circuit->fd, (void *)&llc, LLC_LEN, MSG_PEEK,
242 (struct sockaddr *)&s_addr, (socklen_t *)&addr_len);
243
244 if ((bytesread < 0)
245 || (s_addr.sll_ifindex != (int)circuit->interface->ifindex)) {
246 if (bytesread < 0) {
247 zlog_warn(
248 "isis_recv_packet_bcast(): ifname %s, fd %d, "
249 "bytesread %d, recvfrom(): %s",
250 circuit->interface->name, circuit->fd,
251 bytesread, safe_strerror(errno));
252 }
253 if (s_addr.sll_ifindex != (int)circuit->interface->ifindex) {
254 zlog_warn(
255 "packet is received on multiple interfaces: "
256 "socket interface %d, circuit interface %d, "
257 "packet type %u",
258 s_addr.sll_ifindex, circuit->interface->ifindex,
259 s_addr.sll_pkttype);
260 }
261
262 /* get rid of the packet */
263 bytesread = recvfrom(circuit->fd, discard_buff,
264 sizeof(discard_buff), MSG_DONTWAIT,
265 (struct sockaddr *)&s_addr,
266 (socklen_t *)&addr_len);
267
268 if (bytesread < 0)
269 zlog_warn("isis_recv_pdu_bcast(): recvfrom() failed");
270
271 return ISIS_WARNING;
272 }
273 /*
274 * Filtering by llc field, discard packets sent by this host (other
275 * circuit)
276 */
277 if (!llc_check(llc) || s_addr.sll_pkttype == PACKET_OUTGOING) {
278 /* Read the packet into discard buff */
279 bytesread = recvfrom(circuit->fd, discard_buff,
280 sizeof(discard_buff), MSG_DONTWAIT,
281 (struct sockaddr *)&s_addr,
282 (socklen_t *)&addr_len);
283 if (bytesread < 0)
284 zlog_warn("isis_recv_pdu_bcast(): recvfrom() failed");
285 return ISIS_WARNING;
286 }
287
288 /* on lan we have to read to the static buff first */
289 bytesread = recvfrom(circuit->fd, sock_buff, sizeof(sock_buff),
290 MSG_DONTWAIT, (struct sockaddr *)&s_addr,
291 (socklen_t *)&addr_len);
292 if (bytesread < 0) {
293 zlog_warn("isis_recv_pdu_bcast(): recvfrom() failed");
294 return ISIS_WARNING;
295 }
296
297 /* then we lose the LLC */
298 stream_write(circuit->rcv_stream, sock_buff + LLC_LEN,
299 bytesread - LLC_LEN);
300
301 memcpy(ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
302
303 return ISIS_OK;
304 }
305
306 int isis_recv_pdu_p2p(struct isis_circuit *circuit, u_char *ssnpa)
307 {
308 int bytesread, addr_len;
309 struct sockaddr_ll s_addr;
310
311 memset(&s_addr, 0, sizeof(struct sockaddr_ll));
312 addr_len = sizeof(s_addr);
313
314 /* we can read directly to the stream */
315 stream_recvfrom(circuit->rcv_stream, circuit->fd,
316 circuit->interface->mtu, 0, (struct sockaddr *)&s_addr,
317 (socklen_t *)&addr_len);
318
319 if (s_addr.sll_pkttype == PACKET_OUTGOING) {
320 /* Read the packet into discard buff */
321 bytesread = recvfrom(circuit->fd, discard_buff,
322 sizeof(discard_buff), MSG_DONTWAIT,
323 (struct sockaddr *)&s_addr,
324 (socklen_t *)&addr_len);
325 if (bytesread < 0)
326 zlog_warn("isis_recv_pdu_p2p(): recvfrom() failed");
327 return ISIS_WARNING;
328 }
329
330 /* If we don't have protocol type 0x00FE which is
331 * ISO over GRE we exit with pain :)
332 */
333 if (ntohs(s_addr.sll_protocol) != 0x00FE) {
334 zlog_warn("isis_recv_pdu_p2p(): protocol mismatch(): %X",
335 ntohs(s_addr.sll_protocol));
336 return ISIS_WARNING;
337 }
338
339 memcpy(ssnpa, &s_addr.sll_addr, s_addr.sll_halen);
340
341 return ISIS_OK;
342 }
343
344 int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
345 {
346 struct msghdr msg;
347 struct iovec iov[2];
348
349 /* we need to do the LLC in here because of P2P circuits, which will
350 * not need it
351 */
352 struct sockaddr_ll sa;
353
354 stream_set_getp(circuit->snd_stream, 0);
355 memset(&sa, 0, sizeof(struct sockaddr_ll));
356 sa.sll_family = AF_PACKET;
357
358 size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN;
359 sa.sll_protocol = htons(isis_ethertype(frame_size));
360 sa.sll_ifindex = circuit->interface->ifindex;
361 sa.sll_halen = ETH_ALEN;
362 /* RFC5309 section 4.1 recommends ALL_ISS */
363 if (circuit->circ_type == CIRCUIT_T_P2P)
364 memcpy(&sa.sll_addr, ALL_ISS, ETH_ALEN);
365 else if (level == 1)
366 memcpy(&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
367 else
368 memcpy(&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
369
370 /* on a broadcast circuit */
371 /* first we put the LLC in */
372 sock_buff[0] = 0xFE;
373 sock_buff[1] = 0xFE;
374 sock_buff[2] = 0x03;
375
376 memset(&msg, 0, sizeof(msg));
377 msg.msg_name = &sa;
378 msg.msg_namelen = sizeof(struct sockaddr_ll);
379 msg.msg_iov = iov;
380 msg.msg_iovlen = 2;
381 iov[0].iov_base = sock_buff;
382 iov[0].iov_len = LLC_LEN;
383 iov[1].iov_base = circuit->snd_stream->data;
384 iov[1].iov_len = stream_get_endp(circuit->snd_stream);
385
386 if (sendmsg(circuit->fd, &msg, 0) < 0) {
387 zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
388 circuit->interface->name, safe_strerror(errno));
389 if (ERRNO_IO_RETRY(errno))
390 return ISIS_WARNING;
391 return ISIS_ERROR;
392 }
393 return ISIS_OK;
394 }
395
396 int isis_send_pdu_p2p(struct isis_circuit *circuit, int level)
397 {
398 struct sockaddr_ll sa;
399 ssize_t rv;
400
401 stream_set_getp(circuit->snd_stream, 0);
402 memset(&sa, 0, sizeof(struct sockaddr_ll));
403 sa.sll_family = AF_PACKET;
404 sa.sll_ifindex = circuit->interface->ifindex;
405 sa.sll_halen = ETH_ALEN;
406 if (level == 1)
407 memcpy(&sa.sll_addr, ALL_L1_ISS, ETH_ALEN);
408 else
409 memcpy(&sa.sll_addr, ALL_L2_ISS, ETH_ALEN);
410
411
412 /* lets try correcting the protocol */
413 sa.sll_protocol = htons(0x00FE);
414 rv = sendto(circuit->fd, circuit->snd_stream->data,
415 stream_get_endp(circuit->snd_stream), 0,
416 (struct sockaddr *)&sa, sizeof(struct sockaddr_ll));
417 if (rv < 0) {
418 zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s",
419 circuit->interface->name, safe_strerror(errno));
420 if (ERRNO_IO_RETRY(errno))
421 return ISIS_WARNING;
422 return ISIS_ERROR;
423 }
424 return ISIS_OK;
425 }
426
427 #endif /* ISIS_METHOD == ISIS_METHOD_PFPACKET */