]> git.ipfire.org Git - people/ms/rstp.git/blob - packet.c
Simple usermode helper script
[people/ms/rstp.git] / packet.c
1 /*****************************************************************************
2 Copyright (c) 2006 EMC Corporation.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2 of the License, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 more details.
13
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc., 59
16 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 The full GNU General Public License is included in this distribution in the
19 file called LICENSE.
20
21 Authors: Srinivas Aji <Aji_Srinivas@emc.com>
22 Stephen Hemminger <shemminger@linux-foundation.org>
23
24 ******************************************************************************/
25
26 #include "packet.h"
27 #include "epoll_loop.h"
28 #include "netif_utils.h"
29 #include "bridge_ctl.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <stdint.h>
37 #include <sys/socket.h>
38 #include <sys/ioctl.h>
39 #include <fcntl.h>
40 #include <netinet/in.h>
41
42 #include <linux/if.h>
43 #include <linux/if_ether.h>
44 #include <linux/if_packet.h>
45 #include <linux/filter.h>
46
47 #include "log.h"
48
49 static struct epoll_event_handler packet_event;
50
51 #ifdef STP_DBG
52 static void dump_packet(const unsigned char *buf, int cc)
53 {
54 int i, j;
55 for (i = 0; i < cc; i += 16) {
56 for (j = 0; j < 16 && i + j < cc; j++)
57 printf(" %02x", buf[i + j]);
58 printf("\n");
59 }
60 printf("\n");
61 fflush(stdout);
62 }
63 #endif
64
65 /*
66 * To send/receive Spanning Tree packets we use PF_PACKET because
67 * it allows the filtering we want but gives raw data
68 */
69 void packet_send(int ifindex, const unsigned char *data, int len)
70 {
71 int l;
72 struct sockaddr_ll sl = {
73 .sll_family = AF_PACKET,
74 .sll_protocol = htons(ETH_P_802_2),
75 .sll_ifindex = ifindex,
76 .sll_halen = ETH_ALEN,
77 };
78
79 memcpy(sl.sll_addr, data, ETH_ALEN);
80
81 #ifdef STP_DBG
82 printf("Send to %02x:%02x:%02x:%02x:%02x:%02x\n",
83 sl.sll_addr[0], sl.sll_addr[1], sl.sll_addr[2],
84 sl.sll_addr[3], sl.sll_addr[4], sl.sll_addr[5]);
85 dump_packet(data, len);
86 #endif
87 l = sendto(packet_event.fd, data, len, 0,
88 (struct sockaddr *) &sl, sizeof(sl));
89
90 if (l < 0) {
91 if (errno != EWOULDBLOCK)
92 ERROR("send failed: %m");
93 } else if (l != len)
94 ERROR("short write in sendto: %d instead of %d", l, len);
95 }
96
97 static void packet_rcv(uint32_t events, struct epoll_event_handler *h)
98 {
99 int cc;
100 unsigned char buf[2048];
101 struct sockaddr_ll sl;
102 socklen_t salen = sizeof sl;
103
104 cc = recvfrom(h->fd, &buf, sizeof(buf), 0,
105 (struct sockaddr *) &sl, &salen);
106 if (cc <= 0) {
107 ERROR("recvfrom failed: %m");
108 return;
109 }
110
111 #ifdef STP_DBG
112 printf("Receive Src %02x:%02x:%02x:%02x:%02x:%02x\n",
113 buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]);
114
115 dump_packet(buf, cc);
116 #endif
117
118 bridge_bpdu_rcv(sl.sll_ifindex, buf, cc);
119 }
120
121 /* Berkeley Packet filter code to filter out spanning tree packets.
122 from tcpdump -dd stp
123 */
124 static struct sock_filter stp_filter[] = {
125 { 0x28, 0, 0, 0x0000000c },
126 { 0x25, 3, 0, 0x000005dc },
127 { 0x30, 0, 0, 0x0000000e },
128 { 0x15, 0, 1, 0x00000042 },
129 { 0x6, 0, 0, 0x00000060 },
130 { 0x6, 0, 0, 0x00000000 },
131 };
132
133 /*
134 * Open up a raw packet socket to catch all 802.2 packets.
135 * and install a packet filter to only see STP (SAP 42)
136 *
137 * Since any bridged devices are already in promiscious mode
138 * no need to add multicast address.
139 */
140 int packet_sock_init(void)
141 {
142 int s;
143 struct sock_fprog prog = {
144 .len = sizeof(stp_filter) / sizeof(stp_filter[0]),
145 .filter = stp_filter,
146 };
147
148 s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_802_2));
149 if (s < 0) {
150 ERROR("socket failed: %m");
151 return -1;
152 }
153
154 if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0)
155 ERROR("setsockopt packet filter failed: %m");
156
157 else if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
158 ERROR("fcntl set nonblock failed: %m");
159
160 else {
161 packet_event.fd = s;
162 packet_event.handler = packet_rcv;
163
164 if (add_epoll(&packet_event) == 0)
165 return 0;
166 }
167
168 close(s);
169 return -1;
170 }