1/*-
2 * Copyright (c) 2016 Yandex LLC
3 * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/mbuf.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/socket.h>
36#include <net/ethernet.h>
37#include <net/if.h>
38#include <net/if_pflog.h>
39#include <net/if_var.h>
40#include <net/if_clone.h>
41#include <net/if_types.h>
42#include <net/vnet.h>
43#include <net/bpf.h>
44
45#include <netinet/in.h>
46#include <netinet/ip_fw.h>
47#include <netinet/ip_var.h>
48#include <netpfil/ipfw/ip_fw_private.h>
49
50VNET_DEFINE_STATIC(struct ifnet *, log_if);
51VNET_DEFINE_STATIC(struct ifnet *, pflog_if);
52VNET_DEFINE_STATIC(struct if_clone *, ipfw_cloner);
53VNET_DEFINE_STATIC(struct if_clone *, ipfwlog_cloner);
54#define	V_ipfw_cloner		VNET(ipfw_cloner)
55#define	V_ipfwlog_cloner	VNET(ipfwlog_cloner)
56#define	V_log_if		VNET(log_if)
57#define	V_pflog_if		VNET(pflog_if)
58
59static const char ipfwname[] = "ipfw";
60static const char ipfwlogname[] = "ipfwlog";
61
62static int
63ipfw_bpf_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
64{
65
66	return (EINVAL);
67}
68
69static int
70ipfw_bpf_output(struct ifnet *ifp, struct mbuf *m,
71	const struct sockaddr *dst, struct route *ro)
72{
73
74	if (m != NULL)
75		FREE_PKT(m);
76	return (0);
77}
78
79static void
80ipfw_clone_destroy(struct ifnet *ifp)
81{
82
83	if (ifp->if_hdrlen == ETHER_HDR_LEN)
84		V_log_if = NULL;
85	else
86		V_pflog_if = NULL;
87
88	NET_EPOCH_WAIT();
89	bpfdetach(ifp);
90	if_detach(ifp);
91	if_free(ifp);
92}
93
94static int
95ipfw_clone_create(struct if_clone *ifc, int unit, caddr_t params)
96{
97	struct ifnet *ifp;
98
99	ifp = if_alloc(IFT_PFLOG);
100	if (ifp == NULL)
101		return (ENOSPC);
102	if_initname(ifp, ipfwname, unit);
103	ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
104	ifp->if_mtu = 65536;
105	ifp->if_ioctl = ipfw_bpf_ioctl;
106	ifp->if_output = ipfw_bpf_output;
107	ifp->if_hdrlen = ETHER_HDR_LEN;
108	if_attach(ifp);
109	bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN);
110	if (V_log_if != NULL) {
111		bpfdetach(ifp);
112		if_detach(ifp);
113		if_free(ifp);
114		return (EEXIST);
115	}
116	V_log_if = ifp;
117	return (0);
118}
119
120static int
121ipfwlog_clone_create(struct if_clone *ifc, int unit, caddr_t params)
122{
123	struct ifnet *ifp;
124
125	ifp = if_alloc(IFT_PFLOG);
126	if (ifp == NULL)
127		return (ENOSPC);
128	if_initname(ifp, ipfwlogname, unit);
129	ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
130	ifp->if_mtu = 65536;
131	ifp->if_ioctl = ipfw_bpf_ioctl;
132	ifp->if_output = ipfw_bpf_output;
133	ifp->if_hdrlen = PFLOG_HDRLEN;
134	if_attach(ifp);
135	bpfattach(ifp, DLT_PFLOG, PFLOG_HDRLEN);
136	if (V_pflog_if != NULL) {
137		bpfdetach(ifp);
138		if_detach(ifp);
139		if_free(ifp);
140		return (EEXIST);
141	}
142	V_pflog_if = ifp;
143	return (0);
144}
145
146void
147ipfw_bpf_tap(u_char *pkt, u_int pktlen)
148{
149	struct ifnet *ifp = V_log_if;
150
151	NET_EPOCH_ASSERT();
152	if (ifp != NULL)
153		BPF_TAP(ifp, pkt, pktlen);
154}
155
156void
157ipfw_bpf_mtap(struct mbuf *m)
158{
159	struct ifnet *ifp = V_log_if;
160
161	NET_EPOCH_ASSERT();
162	if (ifp != NULL)
163		BPF_MTAP(ifp, m);
164}
165
166void
167ipfw_bpf_mtap2(void *data, u_int dlen, struct mbuf *m)
168{
169	struct ifnet *logif;
170
171	NET_EPOCH_ASSERT();
172	switch (dlen) {
173	case (ETHER_HDR_LEN):
174		logif = V_log_if;
175		break;
176	case (PFLOG_HDRLEN):
177		logif = V_pflog_if;
178		break;
179	default:
180#ifdef INVARIANTS
181		panic("%s: unsupported len %d", __func__, dlen);
182#endif
183		logif = NULL;
184	}
185
186	if (logif != NULL)
187		BPF_MTAP2(logif, data, dlen, m);
188}
189
190void
191ipfw_bpf_init(int first __unused)
192{
193
194	V_log_if = NULL;
195	V_pflog_if = NULL;
196	V_ipfw_cloner = if_clone_simple(ipfwname, ipfw_clone_create,
197	    ipfw_clone_destroy, 0);
198	V_ipfwlog_cloner = if_clone_simple(ipfwlogname, ipfwlog_clone_create,
199	    ipfw_clone_destroy, 0);
200}
201
202void
203ipfw_bpf_uninit(int last __unused)
204{
205
206	if_clone_detach(V_ipfw_cloner);
207	if_clone_detach(V_ipfwlog_cloner);
208}
209